package com.persagy.proxy.adm.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.model.QueryCriteria;
import com.persagy.dmp.basic.model.QueryOperator;
import com.persagy.dmp.common.constant.ValidEnum;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.proxy.adm.request.AdmQueryCriteria;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ADM通用查询条件
 * @author Charlie Yu
 * @date 2021-08-16
 */
public class AdmQueryCriteriaHelper {

    /** 条件匹配字符串 */
    public static final String[] COND_STR = {" = "," != "," > "," >= "," < "," <= "," in "," isnull "," contain "," startwith "," endwith "};
    /** 条件匹配字符串 根据ADM实际调用查看，去掉空格*/
    public static final String[] COND_STR_TRIM = {"=","!=",">",">=","<","<=","contain","isnull","in","startwith","endwith"};
    /** 需要替换的字段对照 Map<AdmQueryCriteria.name, Map<AdmFieldName, DmpFieldName> */
    public static Map<String, Map<String, String>> RENAME_MAP = new HashMap<>(16);
    /** 与数据中台的操作参数对照 */
    public static Map<String, String> OPERATOR_MAP = new HashMap<>();

    static {
        OPERATOR_MAP.put("!=", "$ne");
        OPERATOR_MAP.put(">", "$gt");
        OPERATOR_MAP.put(">=", "$gte");
        OPERATOR_MAP.put("<", "$lt");
        OPERATOR_MAP.put("<=", "$lte");
        OPERATOR_MAP.put("in", "$in");
        OPERATOR_MAP.put("contain", "$like");
        OPERATOR_MAP.put("startwith", "$likeRight");
        OPERATOR_MAP.put("endwith", "$likeLeft");
    }

    /**
     * 将ADM的查询条件转换为DMP的
     * 调用前注意：
     * - 调用前将name赋值，才会将条件中adm的属性替换为dmp的属性
     * - 统计查询时将isOnlyCount设置为true
     * 调用后注意（需要调用者自行处理的）：
     * - 不处理级联，级联应在调用者里处理级联调用转换后查询赋值
     * - 不处理projection，查询后自行根据projection筛选返回属性。（dmp只能指定扩展属性，而adm的是针对所有属性的）
     * @param admCriteria
     * @return
     */
    public static QueryCriteria toDmpCriteria(AdmQueryCriteria admCriteria) {
        QueryCriteria dmpCriteria = new QueryCriteria();
        // 拷贝分页
        copyPageInfo(admCriteria, dmpCriteria);
        // 处理filters
        ObjectNode criteria = toCriteria(admCriteria.getName(), admCriteria.getFilters());
        dmpCriteria.setCriteria(criteria);
        if(StrUtil.isNotEmpty(admCriteria.getName())){
            // 处理name
            criteria.put("objType", admCriteria.getName());
        }
        // 补充valid = 1
        if(!criteria.has(BaseEntity.PROP_VALID)) {
            criteria.put(BaseEntity.PROP_VALID, ValidEnum.TRUE.getType());
        }
        // 补充关系条件
        if(admCriteria.getRelationFrom() != null) {
            criteria.set(QueryOperator.RELATION_FROM.getIndex(), admCriteria.getRelationFrom());
        }
        if(admCriteria.getRelationTo() != null) {
            criteria.set(QueryOperator.RELATION_TO.getIndex(), admCriteria.getRelationTo());
        }
        // 处理排序
        dmpCriteria.setOrders(toOrderItem(admCriteria.getOrders()));
        // 处理统计
        dmpCriteria.setOnlyCount(admCriteria.isOnlyCount());
        return dmpCriteria;
    }

    /**
     * 处理分页信息
     * @param admCriteria
     * @param dmpCriteria
     */
    public static void copyPageInfo(AdmQueryCriteria admCriteria, QueryCriteria dmpCriteria) {
        // 页码
        Integer pageNumber = admCriteria.getPageNumber();
        if(pageNumber == null || pageNumber < 1) {
            pageNumber = 1;
        }
        dmpCriteria.setPage(pageNumber.longValue());
        // 条数
        Integer pageSize = admCriteria.getPageSize();
        if(pageSize == null || pageSize > admCriteria.getMaxRow()) {
            pageSize = admCriteria.getMaxRow();
        }
        dmpCriteria.setSize(pageSize.longValue());
    }

    /**
     * 排序字符串转换为对象
     * @param orderStr
     */
    public static List<OrderItem> toOrderItem(String orderStr) {
        if(StrUtil.isBlank(orderStr)) {
            return null;
        }
        // 2021年10月20日16:00:01 兼容DMP中台的createTime,updateTime字段
        orderStr = StrUtil.replace(orderStr,"createTime","creationTime");
        orderStr = StrUtil.replace(orderStr,"updateTime","modifiedTime");
        // 按,分隔
        String[] orders = StrUtil.split(orderStr, ",");
        List<OrderItem> orderList = new ArrayList<>();
        for(String order:orders) {
            // 按空格分隔
            String[] items = StrUtil.split(order, " ");
            // 去除空串
            items = ArrayUtil.removeEmpty(items);
            if(ArrayUtil.isEmpty(items)) {
                continue;
            }
            OrderItem orderItem = new OrderItem();
            // 第一位为排序字段
            orderItem.setColumn(items[0].trim());
            // 只有一位 或 第二位不是desc  表示为升序
            orderItem.setAsc(items.length <= 1 || !"desc".equalsIgnoreCase(items[1]));
            orderList.add(orderItem);
        }
        return orderList;
    }

    /**
     * 将ADM过滤条件转换为DMP查询
     * 由于数据中台不支持or条件，因此不能转换为中台的KV条件形式，只能转换为sql
     * @param filters
     * @return
     */
    public static ObjectNode toCriteria(String name, String filters) {
        ObjectNode criteria = JsonNodeFactory.instance.objectNode();
        // filters格式参考：http://doc.sagacloud.cn/doc/service/dev/lib/web/filters.html#%E8%BF%87%E6%BB%A4%E6%9D%A1%E4%BB%B6
        if(StrUtil.isBlank(filters)) {
            return criteria;
        }
        // 替换逻辑运算符： &&  ||  ;
        filters = StrUtil.replace(filters, "&&", " and ");
        filters = StrUtil.replace(filters, "||", " or ");
        filters = StrUtil.replace(filters, ";", " and ");
        // 处理[]
        filters = StrUtil.replace(filters, "[", " ( ");
        filters = StrUtil.replace(filters, "]", " ) ");
        // 查找下一个 连接点[and or为连接点]
        convertCondition(name, criteria, filters);
        return criteria;
    }

    /**
     * 转换为查询条件
     * @param name
     * @param criteria
     * @param filters
     */
    private static void convertCondition(String name, ObjectNode criteria, String filters) {
        // 按and分隔
        List<String> andFilters = StrUtil.splitTrim(filters, " and ");
        // 按分隔后的表达式依次处理
        for(String andFilter:andFilters) {
            // 按or分组
            List<String> orFilters = StrUtil.splitTrim(andFilter, " or ");
            if(CollUtil.isEmpty(orFilters)) {
                continue;
            }
            // 长度为1，则不是or条件
            if(orFilters.size() == 1) {
                // 转换为Condition
                convertExpress(name, criteria, orFilters.get(0));
                continue;
            }
            // or条件
            ObjectNode andOr = (ObjectNode) criteria.get(QueryOperator.AND_OR.getIndex());
            if(andOr == null) {
                // 没有此条件则创建
                andOr = criteria.putObject(QueryOperator.AND_OR.getIndex());
            }
            // 转换为Condition
            for(String orFilter:orFilters) {
                convertExpress(name, andOr, orFilter);
            }
        }
    }

    /**
     * 处理表达式
     * @param criteria
     * @param express
     */
    private static void convertExpress(String name, ObjectNode criteria, String express) {
        String cond = StrUtil.isBlank(StrUtil.getContainsStrIgnoreCase(express, COND_STR)) ?
                StrUtil.getContainsStrIgnoreCase(express, COND_STR_TRIM) : StrUtil.getContainsStrIgnoreCase(express, COND_STR);
        if(StrUtil.isBlank(cond)) {
            return;
        }
        int condIndex = StrUtil.indexOfIgnoreCase(express, cond, 0);
        String condLeft = StrUtil.subPre(express, condIndex).trim();
        String condRight = StrUtil.subSuf(express, condIndex+cond.length()).trim();
        addCondition(name, criteria, condLeft, cond.trim().toLowerCase(), condRight);
    }

    /**
     * 拼接条件
     * @param criteria
     * @param column
     * @param operator
     * @param value
     */
    private static void addCondition(String name, ObjectNode criteria, String column, String operator, String value) {
        // 20211015 移除列名中的infos.
        column = StrUtil.replaceIgnoreCase(column, "infos.", "");
        // 替换列名
        column = replaceColumns(name, column);
        if("=".equals(operator)) {
            criteria.set(column, getJsonNode(criteria, value));
        } else if("in".equals(operator)) {
            // 按,分隔
            String[] splits = StrUtil.split(value, ",");
            if(ArrayUtil.isEmpty(splits)) {
                return;
            }
            ArrayNode node = criteria.putArray(column);
            for(String split:splits) {
                // 去掉所有的() 即为值
                split = StrUtil.removeAll(split, '(',')');
                node.add(getJsonNode(criteria, split.trim()));
            }
        } else if("isnull".equals(operator)) {
            boolean ifNot = true;
            if(column.indexOf("not") >= 0){
                ifNot = false;
                column = column.replace("not","").trim();
            }
            ObjectNode node = criteria.putObject(column);
            node.put("$null", ifNot);
        } else {
            ObjectNode node = criteria.putObject(column);
            node.set(OPERATOR_MAP.get(operator), getJsonNode(node, value));
        }
     }

    /**
     * 取Json值对象
     * @param criteria
     * @param value
     * @return
     */
    private static JsonNode getJsonNode(ObjectNode criteria, String value) {
        if(StrUtil.isBlank(value)) {
            return criteria.nullNode();
        }
        // 字符串
        if(StrUtil.startWith(value, "'") && StrUtil.endWith(value, "'")) {
            return criteria.textNode(StrUtil.sub(value, 1, value.length()-1));
        }
        // 字符串
        if(StrUtil.startWith(value, "\"") && StrUtil.endWith(value, "\"")) {
            return criteria.textNode(StrUtil.sub(value, 1, value.length()-1));
        }
        // 数值
        if (StrUtil.contains(value, ".")) {
            try {
                BigDecimal no = new BigDecimal(value);
                return criteria.numberNode(no);
            } catch (NumberFormatException e) {

            }
        }
        // 整型
        try {
            Integer no = Integer.valueOf(value);
            return criteria.numberNode(no);
        } catch (NumberFormatException e) {

        }
        return criteria.nullNode();
    }

    /**
     * 替换属性称名
     * @param name
     * @param column
     * @return
     */
    private static String replaceColumns(String name, String column) {
        Map<String, String> fieldMap = getRenameFields(name);
        // 没有对照则使用原本的
        if(MapUtil.isEmpty(fieldMap) || !fieldMap.containsKey(column)) {
            return column;
        }
        return fieldMap.get(column);
    }

    /**
     * 获取更名对照
     * @param name
     * @return
     */
    private static Map<String, String> getRenameFields(String name) {
        // 获取通用对照
        Map<String, String> commonMap = getRenameMap().get("COMMON");
        // 获取此类型的对照
        Map<String, String> nameMap = getRenameMap().get(name);
        if(MapUtil.isEmpty(nameMap)) {
            return commonMap;
        }
        Map<String, String> rsMap = new HashMap<>();
        rsMap.putAll(commonMap);
        rsMap.putAll(nameMap);
        return rsMap;
    }

    /**
     * 获取通用Map
     * @return
     */
    private static Map<String, Map<String, String>> getRenameMap() {
        if(MapUtil.isEmpty(RENAME_MAP)) {
            // COMMON表示通用对照
            Map<String, String> commonMap = new HashMap<>();
            commonMap.put("createTime", "creationTime");
            commonMap.put("lastUpdate", "modifiedTime");
            RENAME_MAP.put("COMMON", commonMap);
        }
        return RENAME_MAP;
    }
}
