package com.pictc.jdbc.model;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.apache.commons.compress.utils.Sets;
import com.pictc.utils.DateUtils;
import com.pictc.utils.StringUtils;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

@ApiModel
@Data
@Accessors(chain = true)
public class Criteria implements Serializable{
	
	private static final long serialVersionUID = 1L;
	
	private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
	
	private static final Set<Class<?>> DoubleClass = Sets.newHashSet(Double.class,double.class,float.class,Float.class,BigDecimal.class);
	
	private static final Set<Class<?>> DateClass = Sets.newHashSet(Date.class,Timestamp.class,java.sql.Date.class,Time.class);
	
	private static final Set<Class<?>> IntClass = Sets.newHashSet(int.class,Integer.class,byte.class,Byte.class,short.class,Short.class,long.class,Long.class,BigInteger.class);

	public enum Type{
		EQ("="),LIKE("LIKE"),GT(">"),EQ_GT(">="),LT("<"),EQ_LT("<="),
		BETWEEN("BETWEEN"),IN("IN"),NULL("IS NULL");
		
		private String sql;
		
		private Type(String sql) {
			this.sql = sql;
		}

		public String getSql() {
			return sql;
		}
		
	}
	
	public enum DataType{
		DATE,INTEGER,DOUBLE,STRING
	}
	
	@ApiModelProperty(name = "条件连接条件，默认为AND")
	private Link link = Link.AND;
	
	@ApiModelProperty(name = "条件是否取反")
	private boolean not;
	
	@ApiModelProperty(name = "属性名")
	private String field;
	
	@ApiModelProperty(name = "条件操作符，默认为EQ")
	private Type type = Type.EQ;
	
	private DataType dataType = null; //数据类型
	
	@ApiModelProperty(name = "条件值")
	private Object val;
	
	@ApiModelProperty(name = "条件第二值")
	private Object secondVal;
	
	@ApiModelProperty(name = "条件in的List值")
	private List<Object> listVal;
	
	private String sql;
	
	public JdbcType getJdbc() {
		switch (dataType) {
			case DATE:  return JdbcType.TIMESTAMP;
			case INTEGER:  return JdbcType.INTEGER;
			case DOUBLE:  return JdbcType.DECIMAL;
			default:  return JdbcType.VARCHAR;
		}
	}
	
	public Object getVal() {
		if(val!=null) {
			return parseVal(val);
		}
		return null;
	}
	
	public Criteria setVal(Object val) {
		this.val = val;
		parseDataType(val);
		return this;
	}
	
	public Object getSecondVal() {
		if(secondVal!=null) {
			return parseVal(secondVal);
		}
		return null;
	}
	
	public List<Object> getListVal() {
		if(listVal!=null && !listVal.isEmpty()) {
			List<Object> res = new ArrayList<Object>(listVal.size());
			for (Object _lval : listVal) {
				res.add(parseVal(_lval));
			}
			return res;
		}
		return null;
	}
	
	public Criteria setListVal(List<Object> listVal) {
		this.listVal = listVal;
		if(listVal!=null && !listVal.isEmpty()) {
			Object object = listVal.get(0);
			parseDataType(object);
		}
		return this;
	}
	
	private void parseDataType(Object _val) {
		if(dataType==null) {
			if(_val!=null) {
				Class<?> clazz = _val.getClass();
				if(DoubleClass.contains(clazz)) {
					dataType = DataType.DOUBLE;
				}else if(DateClass.contains(clazz)) {
					dataType = DataType.DATE;
				}else if(IntClass.contains(clazz)) {
					dataType = DataType.INTEGER;
				}else {
					dataType = DataType.STRING;
				}
				
			}
		}
	}
	
	private Object parseVal(Object _val) {
		String _str = _val.toString();
		if(StringUtils.isEmpty(_str)) return null;
		if(dataType==DataType.DATE) {
			if(_val instanceof Timestamp || _val instanceof Date) {
				return _val;
			}else if(_str.matches("^\\d+$")) {
				return new Timestamp(Long.valueOf(_str));
			}
			return Timestamp.from(DateUtils.stringToDate(_str,DATE_FORMAT).toInstant());
		}else if(dataType==DataType.DOUBLE) {
			if(_val instanceof BigDecimal) {
				return _val;
			}
			return new BigDecimal(_str);
		}else if(dataType==DataType.INTEGER) {
			if(_val instanceof Integer) {
				return _val;
			}
			return Integer.valueOf(_str);
		}
		return _str;
	}
	
	public boolean isBetween() {
		return type==Type.BETWEEN;
	}
	
	public boolean isNull() {
		return type==Type.NULL;
	}
	
	public boolean isListValue() {
		return type==Type.IN;
	}
	
	public boolean hasSql() {
		return !StringUtils.isEmpty(sql);
	}
}
