package cn.orm.util.builder;

import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;

import cn.config.SysConfig;
import cn.constant.CharsetKit;
import cn.constant.SqlConst;
import cn.orm.abs.QueryRuleEnum;
import cn.orm.sqltoy.model.EntityQuery;
import cn.orm.sqltoy.model.EntityUpdate;
import cn.orm.util.metadata.DBMetaData;
import cn.orm.util.metadata.base.Field;
import cn.util.core.IsKit;
import cn.util.core.LogKit;
import cn.util.core.TypeKit;
import cn.util.string.JsonKit;
import cn.util.string.NamingKit;
import cn.util.time.TimeKit;

/**QueryWrapper依据页面传来的查询条件参数paramsMap组装成WHERE子句: 
 * {
 *  "ipaddr":"*11*","userName":"*22*","status":"1",
 *  "params":{"beginTime":"2022-01-05T16:00:00.000Z","endTime":"2022-02-13T16:00:00.000Z"}
 * } 
 * 其中：params={}是诸如时间等一个取值范围的参数；*表示LIKE
 * 
 * 目前：仅支持一个params
 * 
 * 关于规则：
 * 	     处理规则
		QueryRuleEnum rule = convert2Rule(value);
		//替换成SQL的操作符号
		value = replaceValue(rule, value);
		//组装where句子
		where = addEasyQuery(key, rule, value);

	目前支持的运算符op：
	0) 默认：表示=
	1）le(<=),lt(<),ge(>), gt(>=)：比如：map.put(key:"le 2")表示：key <= 2
	2）*：比如map.put(key:"*2*"): 表示：key LIKE 2
	3）in
	4) ! 表示：!=
	5) FUN 例如：map.put(find_in_set,FUN('4',type)) 表示：find_in_set('4',type)
 */
public class BuildWhere
{
	/** 静态代码块获取静态注入变量值*/
	private static boolean humpConversion;
	static {
		humpConversion = new SysConfig().isHumpConversion();
	}

	private static final String BEGIN = "begin";
	private static final String END = "end";


	/**用户给出查询条件ids，组装出对应的where语句*/
	public static String genQueryWrapper(String tableName,String ids)
	{
		if(IsKit.isNotNullOrEmpty(ids)) {
			//页面给出参数值格式严格地为：key|FUNIN(1,2,3) 含义为：(key IN(1,2,3));
			if(ids.contains("FUN")) {
				Map<String, Object> paramMap = new HashMap<String, Object>();
				String[] idArry = TypeKit.strToArray(ids, SqlConst.SYMBOL_VLINE);
				paramMap.put(idArry[0], idArry[1]);
				return genQueryWrapper(tableName,paramMap);
			}
			else {
				return genQueryWrapper(tableName,_buildMapFromIds(ids));
			}
		}
		else {
			return null;	
		}
	}

	//用户给定查询条件upId构造where子句
	public static String genQueryWrapper(String ids)
	{
		return genQueryWrapper(null,ids);
	}
	public static String genQueryWrapper(Map<String, Object> paramMap)
	{
		return genQueryWrapper(null,paramMap,"");
	}
	public static String genQueryWrapper(String tableName,Map<String, Object> paramMap)
	{
		return genQueryWrapper(tableName,paramMap,"");
	}
	public static String genQueryWrapper(String tableName,Map<String, Object> paramMap,String rangeField)
	{
		return _buildQueryWrapper(tableName,paramMap,rangeField," AND ");
	}
	//条件之间默认关系是：AND , 此函数支持OR关系 
	public static String genQueryWrapper_OR(Map<String, Object> paramMap)
	{
		return _buildQueryWrapper(null,paramMap,""," OR ");
	}
	/**
	 * record：记录中的主键及对应的取值
	 * keys：记录中包含的主键
	 * @param op 关系运算符
	 */
	public static String genWhereWrapper(Map<String, Object> record,Object[] keys,String op) 	
	{
		if(IsKit.isNullOrEmpty(keys) || IsKit.isNullOrEmpty(record)) {
			return null;
		}
		StringBuilder builder = new StringBuilder();
		Object _o = record.get(TypeKit.objToStr(keys[0]));
		if(IsKit.isNotNull(_o)) 
		{
			builder.append(TypeKit.objToStr(keys[0])).append(" ='").append(TypeKit.objToStr(_o)).append("'");
			if (keys.length > 1) {
				for (int i = 1,loop = keys.length; i < loop; i++) {
					_o = record.get(TypeKit.objToStr(keys[i]));
					builder.append(" ")
					.append(op).append(" ").append(TypeKit.objToStr(keys[i]))
					.append(" ='").append(TypeKit.objToStr(_o)).append("'");	
				}
			}
		}
		return builder.toString();
	}
	/**多值查询条件语句，即where子句；但不含where关键字
	 * 调用：
	 * QueryGenerator.QueryWrapper(tableName,paramMap,rangeField)
	 * 条件参数paramsMap格式：: 
	 * {
	 *  "ipaddr":"*11*","userName":"*22*","status":"1",
	 *  "params":{"beginTime":"2022-01-05T16:00:00.000Z","endTime":"2022-02-13T16:00:00.000Z"}
	 * }
	 * 其中：Json字符串params={}是区间取值参数比如时间；默认为beginTime和endTime
	 * 页面指定的某个区间查询字段rangeField对应的表中字段（目前支持一个字段）rangeField值由params中
	 * 值确定，其中rangeField：
	 * 1）如果有值比如：login_time：则依据params的产生条件为
	 * 2022-08-31<=login_time<=2022-09-08
	 * 2）如果取值：null：则依据params的产生条件为;
	 *  (begin_time >='2022-09-01')  AND (end_time <='2022-10-04')
	 */
	private static String _buildQueryWrapper(String tableName,
			Map<String, Object> paramMap,String rangeField,String oPType)
	{
		if(IsKit.isNullOrEmpty(paramMap)) {
			return null; 
		}
		/**key转为表中字段的表示方法（下划线） 但不包括params中的变量*/
		if(humpConversion) {
			paramMap = NamingKit.camelToUnderline(paramMap);
		}
		/**移除paramMap中无效字段，即不是数据库表字段的key值，含params参数*/
		//记录"params"参数对应的Java类型的名称
		String className = "";
		if(IsKit.isNotNullOrEmpty(tableName))
		{ //获取表中所有字段
			List<String> tableFields = DBMetaData.getColumnsNameList(tableName);
			//获取记录里的字段名的集合
			Object[] recordFields = (paramMap.keySet()).toArray(String[]::new);
			for (int i = 0; i < recordFields.length; i++) 
			{
				String key = TypeKit.objToStr(recordFields[i]);
				//区间取值的Key处理
				if("params".equalsIgnoreCase(key))
				{
					Field f = DBMetaData.getFields(tableName,rangeField);
					if(IsKit.isNotNullOrEmpty(f)) {
						//字段类型对应的Java类的名称
						className = f.getJavaTypeName();	
					}
					continue;
				}
				if (!IsKit.isInFields(tableFields,key)) {
					paramMap.remove(key);
				}
			}
		}
		//不保证线程安全
		StringBuilder resultBuffer = new StringBuilder();
		String where = "";

		/**遍历paramMap，处理每个条件 在键和值都需要时，常用使用entrySet() */
		for (Map.Entry<String, Object> entry : paramMap.entrySet())
		{
			where = "";
			String key = entry.getKey();
			if(IsKit.isNullOrEmpty(key)){
				continue;
			}
			//空值即值为{}的情况
			Object value = entry.getValue();
			if(IsKit.isNullOrEmpty(value) || "{}".equals(value)) {
				continue;
			}
			/**关于params参数的内容，目前支持的处理格式：
			 * 1）params:{"beginTime":,"endTime":}
			 * 2）params:{"oper_time":"2022-07-05,2022-08-10"} 要求：不含begin和end
			 * 其中：params参数是Json字符串，否则：JsonUtil.json2Map_jackson报错
			 * */
			if("params".equalsIgnoreCase(key))
			{
				String param = TypeKit.objToStr(value);
				if(IsKit.isNotNullOrEmpty(param)){
					Map<String,Object> paramsMap = JsonKit.json2Map_jackson(param);
					if(IsKit.isNullOrEmpty(paramsMap)){
						continue;
					}
					//对每个map的键值对进行处理
					for (Map.Entry<String,Object> jsonEntry : paramsMap.entrySet())
					{
						String jsonKey = jsonEntry.getKey();
						Object jsonValue = jsonEntry.getValue();
						if(IsKit.isNullOrEmpty(jsonKey) || IsKit.isNullOrEmpty(jsonValue)) {
							continue;
						}
						jsonKey = NamingKit.camelToUnderline(jsonKey);
						/**params：{"oper_time":"2022-07-05,2022-08-10"} */
						if(!jsonKey.contains(BEGIN) && !jsonKey.contains(END)) 
						{
							_handleRangeField(jsonKey,jsonValue,oPType,className,resultBuffer,where);
							continue;
						}
						String dataRangField = "";
						if(IsKit.isNullOrEmpty(rangeField)) {
							dataRangField = jsonKey;
						}
						else {
							dataRangField = rangeField;
						}
						//判断是否有区间值
						String endValue = null, beginValue = null;
						if (jsonKey.contains(BEGIN)){  
							beginValue = TypeKit.objToStr(jsonValue).trim();
							where = _addQueryByRule(dataRangField, className, beginValue, QueryRuleEnum.GE);
							if(IsKit.isNotNullOrEmpty(where)) {
								if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
									resultBuffer.append(oPType);	
								}
								resultBuffer.append(where);
							}					
						}
						if (jsonKey.contains(END)) {
							endValue = TypeKit.objToStr(jsonValue).trim();
							where = _addQueryByRule(dataRangField, className, endValue, QueryRuleEnum.LE);
							if(IsKit.isNotNullOrEmpty(where)) {
								if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
									resultBuffer.append(" AND ");	
								}
								resultBuffer.append(where);
							}
						}
					}//for 
					continue;
				}
			}
			/**判断单值  参数带不同标识字符串 走不同的查询。即params参数以外的项*/
			String field = "";
			if(humpConversion) {
				field = NamingKit.camelToUnderline(key);
			}
			/** value前后带逗号。即格式为：",具体值,"
			 *  处理：支持分割后模糊查询 使多选字段的查询生效",";
			 **/
			if (TypeKit.objToStr(value).startsWith(SqlConst.COMMA)	&& TypeKit.objToStr(value).endsWith(SqlConst.COMMA))
			{
				where = _addMultiQuery(field,value);
				if(IsKit.isNotNull(where)){
					if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
						resultBuffer.append(oPType);	
					}
					resultBuffer.append(where);
				}
				continue;
			}
			/** value中间带逗号。即格式为："具体值1,具体值2" 
			 *  含义：key：[具体值1,具体值2] 
			 *  默认处理：具体值1<=key<=具体值2
			 *  主要用于处理elementplus页面上对应表中一个字段区间如时间的选择情况
			 *  SQLConst.SQL_FUN的取值中有可能内含有：,
			 */
			value = TypeKit.objToStr(value);
			if (!TypeKit.objToStr(value).contains(SqlConst.SQL_FUN)
					&& TypeKit.objToStr(value).contains(SqlConst.COMMA))
			{
				/**params：{"oper_time":"2022-07-05,2022-08-10"} */
				_handleRangeField(key,value,oPType,className,resultBuffer,where);
				continue;
			}
			/**
			 * 常规情况：key:value情况处理
			 * 根据参数值带什么关键字符串判断走什么类型的查询
			 * */
			//处理规则
			QueryRuleEnum rule = _convert2Rule(value);
			//替换成SQL的操作符号
			value = _replaceValue(rule, value);
			//组装where句子
			where = _addEasyQuery(key, rule, value);
			if(IsKit.isNotNull(where))	{
				if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
					resultBuffer.append(oPType);	
				}
				resultBuffer.append(where);
			}
		}
		return resultBuffer.toString();
	}
	/**params参数也可能是一个时间字段的区间取值，此字段命名规则不包含begin
	 * 此情况由后边的value中间带逗号。即格式为："具体值1,具体值2"处理
	 * {"oper_time":"2022-07-05,2022-08-10"}
	 */
	private static void _handleRangeField(
			String jsonKey,Object jsonValue,
			String oPType, String className,
			StringBuilder resultBuffer,String where)
	{
		String [] valueArray = (TypeKit.objToStr(jsonValue)).split(",");
		where = _addQueryByRule(jsonKey, className, valueArray[0], QueryRuleEnum.GE);
		if(IsKit.isNotNullOrEmpty(where)) {
			if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
				resultBuffer.append(oPType);	
			}
			resultBuffer.append(where);
		}
		where = _addQueryByRule(jsonKey, className, valueArray[1], QueryRuleEnum.LE);
		if(IsKit.isNotNull(where)) {
			if(IsKit.isNotNullOrEmpty(resultBuffer.toString())) {
				resultBuffer.append(" AND ");	
			}
			resultBuffer.append(where);
		}   
	}
	/**
	 * 根据所传的值 转化成对应的比较方式
	 * 支持><= like in !
	 */
	private static QueryRuleEnum _convert2Rule(Object value) {
		// 避免空数据
		if (IsKit.isNullOrEmpty(value)) {
			return null;
		}
		String val = TypeKit.objToStr(value).trim();
		QueryRuleEnum rule = null;

		if (IsKit.isNull(rule) && val.startsWith(SqlConst.SQL_FUN)) {
			rule = QueryRuleEnum.FUN;
		}

		/**页面带有规则值查询，空格作为分隔符
		 * 此处规则，只适用于 le lt ge gt:
		 * 用法：map.put(key:"le 2"): 表示：key <= 2
		 */
		if (IsKit.isNull(rule) && val.length() >= 3) {
			if (SqlConst.QUERY_SEPARATE_KEYWORD.equals(val.substring(2, 3))) {
				rule = QueryRuleEnum.getByValue(val.substring(0, 2));
			}
		}
		// step 1 .> <
		if (IsKit.isNull(rule) && val.length() >= 2) {
			if (SqlConst.QUERY_SEPARATE_KEYWORD.equals(val.substring(1, 2))) {
				rule = QueryRuleEnum.getByValue(val.substring(0, 1));
			}
		}

		// step 3 like 用法：map.put(key:"*2*"): 表示：key LIKE 2
		if (IsKit.isNull(rule) && val.contains(SqlConst.STAR)) {
			if (val.startsWith(SqlConst.STAR) && val.endsWith(SqlConst.STAR)) {
				rule = QueryRuleEnum.LIKE;
			} else if (val.startsWith(SqlConst.STAR)) {
				rule = QueryRuleEnum.LEFT_LIKE;
			} else if (val.endsWith(SqlConst.STAR)) {
				rule = QueryRuleEnum.RIGHT_LIKE;
			}
		}
		// step 4 in
		if (IsKit.isNull(rule) && val.contains(SqlConst.COMMA)) {
			//查询这里应该有个bug  如果一字段本身就是多选 此时用in查询 未必能查询出来
			rule = QueryRuleEnum.IN;
		}
		//OK step 5 !=   用法：map.put(key:"!2"): 表示：key！=2
		if (IsKit.isNull(rule) && val.startsWith(SqlConst.NOT_EQUAL)) {
			rule = QueryRuleEnum.NE;
		}
		//默认是相等
		return IsKit.isNotNull(rule) ? rule : QueryRuleEnum.EQ;
	}
	/**
	 * 替换掉关键字字符
	 *
	 * @param rule
	 * @param value
	 * @return
	 */
	private static Object _replaceValue(QueryRuleEnum rule, Object value) 
	{
		if (IsKit.isNull(rule)) {
			return null;
		}
		if (!(value instanceof String)) {
			return value;
		}
		String val = TypeKit.objToStr(value).trim();
		if (rule == QueryRuleEnum.LIKE) {
			value = val.substring(1, val.length() - 1);
		} else if (rule == QueryRuleEnum.LEFT_LIKE || rule == QueryRuleEnum.NE) {
			value = val.substring(1);
		} else if (rule == QueryRuleEnum.RIGHT_LIKE) {
			value = val.substring(0, val.length() - 1);
		} else if (rule == QueryRuleEnum.IN) {
			value = val.split(",");
		} else if (rule == QueryRuleEnum.FUN) {
			value = val.substring(3, val.length());
		}
		else {
			if (val.startsWith(rule.getValue())) {
				//此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号，就会被截取的（比如：>=您好）
				value = val.replaceFirst(rule.getValue(), "");
			} else if (val.startsWith(rule.getCondition() + SqlConst.QUERY_SEPARATE_KEYWORD)) {
				value = val.replaceFirst(rule.getCondition() + SqlConst.QUERY_SEPARATE_KEYWORD, "").trim();
			}
		}
		return value;
	}
	/**
	 * 根据规则走不同的查询
	 *
	 * @param queryWrapper QueryWrapper
	 * @param name         字段名字
	 * @param rule         查询规则
	 * @param value        查询条件值
	 */
	private static String _addEasyQuery(String key, QueryRuleEnum rule, Object value)
	{
		if (IsKit.isNullOrEmpty(rule) || IsKit.isNullOrEmpty(value)) {
			return null;
		}
		StringBuilder buffer = new StringBuilder();
		buffer.append("(");
		buffer.append(key);
		switch (rule) {
		case GT:
			buffer.append(" >' ");
			buffer.append(value);
			buffer.append("') ");
			break;
		case GE:
			buffer.append(" >='");
			buffer.append(value);
			buffer.append("') ");
			break;
		case LT:
			buffer.append(" <'");
			buffer.append(value);
			buffer.append("') ");
			break;
		case LE:
			buffer.append(" <='");
			buffer.append(value);
			buffer.append("') ");
			break;
		case EQ:
			buffer.append(" ='");
			buffer.append(value);
			buffer.append("') ");
			break;
		case NE:
			buffer.append(" !='");
			buffer.append(value);
			buffer.append("') ");
			break;
		case IN:
			buffer.append(" IN (");
			if (value instanceof String) {
				buffer.append((Object[]) value.toString().split(","));
			} 
			else if (value instanceof String[]) {
				buffer.append((Object[]) value);
			} else {
				buffer.append(value);
			}
			buffer.append(") ");
			break;
		case LIKE:
			buffer.append(" LIKE '%");
			buffer.append(value);
			buffer.append("%') ");
			break;
		case LEFT_LIKE:
			buffer.append(" LEFT_LIKE '%");
			buffer.append(value);
			buffer.append("%') ");
			break;
		case RIGHT_LIKE:
			buffer.append(" RIGHT_LIKE '%");
			buffer.append(value);
			buffer.append("%') ");
			break;
		case FUN:
			buffer.append(" ");
			buffer.append(value);
			buffer.append(") ");
			break;
		default:
			LogKit.info("--查询规则未匹配到---");
			break;
		}
		return buffer.toString(); 
	}

	private static String _addQueryByRule(String name,String type,String value,QueryRuleEnum rule)  
	{
		Object temp = null;
		if (StringUtils.isNotEmpty(value))
		{
			// 针对数字类型字段，多值查询
			if (value.indexOf(SqlConst.COMMA) != -1) {
				temp = value;
				return _addEasyQuery(name, rule, temp);
			}
			switch (type) {
			case "java.lang.Integer":
				temp = Integer.parseInt(value);
				break;
			case "java.math.BigDecimal":
				temp = new BigDecimal(value);
				break;
			case "java.lang.Short":
				temp = Short.parseShort(value);
				break;
			case "class java.lang.Long":
				temp = Long.parseLong(value);
				break;
			case "java.lang.Float":
				temp = Float.parseFloat(value);
				break;
			case "java.lang.Double":
				temp = Double.parseDouble(value);
				break;
				//"DATE_FORMAT(login_time,'%Y-%m-%d %H:%i:%s')"
			case "java.util.Date":
			case "java.time.LocalDateTime"://SQL:datetime
				temp = value;
				if(IsKit.isNotNullOrEmpty(value) && value.contains("T")) {
					temp = TimeKit.UTCToTimestampStr(value);
				}
				break;
			default:
				temp = value;
				break;
			}
		}
		return _addEasyQuery(name, rule, temp);
	}
	private static String _addMultiQuery(String key,Object value)
	{
		StringBuilder whereBuffer = new StringBuilder(); 
		String multiLikeval = TypeKit.objToStr(value).replace(",,", SqlConst.COMMA);
		String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(SqlConst.COMMA);

		if (vals.length == 1) {
			whereBuffer.append(" (");
			whereBuffer.append(key);
			whereBuffer.append("='");
			whereBuffer.append(vals[0]);
			whereBuffer.append("') ");
		}
		if (vals.length > 1) {
			for(int i = 1; i< vals.length; i++)
			{
				whereBuffer.append(" OR (");
				whereBuffer.append(key);
				whereBuffer.append("='");
				whereBuffer.append(vals[i]);
				whereBuffer.append("') ");
			}
		}
		return whereBuffer.toString();
	}
	/**
	 * 依据用户给出的查询条件ids，返回：map<键,值>
	 * 用户给出查询条件ids格式："键|值,键|值,  ...";	
	 * String[] fieldNameValue = upField.split("\\|");
	 */
	private static Map<String, Object> _buildMapFromIds(String ids)
	{
		Map<String, Object> paramMap = new HashMap<String, Object>();
		/**第一级分割条件为：,*/
		String[] idArry = TypeKit.strToArray(ids, SqlConst.COMMA);
		//	
		if (IsKit.isNotNull(idArry) && idArry.length > 0 )
		{	
			for(int i = 0; i< idArry.length; i++)//比如: shijian|<=IOI123
			{	
				Object rValue = "";
				String lValue = "";				
				/**第二级分割条件为：|
				 * idarray中的元素为：字段名|值
				 */
				String[] keyvalue = TypeKit.strToArray(idArry[i],CharsetKit.SYMBOL_VLine);
				if(IsKit.isNotNull(keyvalue) && (keyvalue.length % 2 == 0)) 
				{
					lValue = keyvalue[0].trim();
					rValue = keyvalue[1].trim();
					/**查询条件取值为空的情况*/
					if(IsKit.isNullOrEmpty(rValue)) {
						continue;
					}
					else {
						paramMap.put(lValue, rValue);
					}					
				}
			}
		}
		return paramMap;
	}
	/////////////////SQLToy////////////////
	/**要求调用者确保whereMap字段都是表内的合法字段*/
	public static EntityUpdate updateWrapper(Map<String,Object> updateMap,Map<String,Object> whereMap)
	{
		EntityUpdate updateWhere = EntityUpdate.create();
		if(IsKit.isNullOrEmpty(updateMap)) {
			return updateWhere;
		}			
		Entry<String, Object> entry = null;
		Iterator<Entry<String, Object>> iter = updateMap.entrySet().iterator();
		while (iter.hasNext()) {
			entry = iter.next();
			updateWhere.set(entry.getKey(), entry.getValue());
		}
		if(IsKit.isNotNullOrEmpty(whereMap)){
			updateWhere.where(genQueryWrapper(whereMap));
		}
		return updateWhere;
	}
	/**查询条件 */
	public static EntityQuery queryWrapper(Map<String,Object> whereMap)
	{
		EntityQuery where = EntityQuery.create();
		if(IsKit.isNotNullOrEmpty(whereMap)){
			where.where(genQueryWrapper(whereMap));
		}
		return where;
	}
}
