package com.persagy.dmp.rwd.digital.utils;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.persagy.dmp.basic.model.QueryOperator;
import com.persagy.dmp.basic.utils.JsonNodeUtils;
import com.persagy.dmp.common.model.entity.BaseEntity;
import com.persagy.dmp.rwd.digital.entity.ObjectDigital;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * 对象 查询条件 助手
 * 增加对对象专属条件的处理
 * @author Charlie Yu
 * @date 2021-08-16
 */
public class ObjectDigitalCriteriaHelper {

    /** 对象类字段集 */
    private static final Set<String> DIGITAL_FIELDS = ReflectUtil.getFieldMap(ObjectDigital.class).keySet();

    /**
     * 拼接扩展条件
     * 暂用#JSON#表示是json条件，不处理驼峰
     * @param criteria
     */
    public static void ensureExtraCond(ObjectNode criteria) {
        if(criteria == null || criteria.isNull()) {
            return;
        }
        ensureSearchCond(criteria);
        ensureRelationCond(criteria);
        ensureJsonCond(criteria);
    }

    /**
     * 拼接json条件
     * @param criteria
     */
    private static void ensureJsonCond(ObjectNode criteria) {
        Iterator<String> fieldNames = criteria.fieldNames();
        Map<String, JsonNode> valueMap = new HashMap<>();
        while (fieldNames.hasNext()) {
            String infoCode = fieldNames.next();
            // 基础属性不用处理
            if(DIGITAL_FIELDS.contains(infoCode)) {
                continue;
            }
            JsonNode value = criteria.get(infoCode);
            if(value == null || value.isNull()) {
                continue;
            }
            // 如果是or条件
            if(QueryOperator.OR.getIndex().equalsIgnoreCase(infoCode)) {
                processOrCond(value);
                continue;
            }
            // 如果值是对象
            if(value.isObject()) {
                // 包含
                if (value.has(QueryOperator.CONTAINS.getIndex())) {
                    JsonNode valueNode = value.get(QueryOperator.CONTAINS.getIndex());
                    String valueStr = valueNode.isTextual() ? value.asText():value.toString();
                    String key = StrUtil.format("#JSON#json_contains({}, {}, '$.{}')", ObjectDigital.EXTRA_COLUMN, valueStr, infoCode);
                    valueMap.put(key, criteria.numberNode(1));
                } else if (value.has(QueryOperator.NOT_CONTAINS.getIndex())) {
                    // 不包含
                    JsonNode valueNode = value.get(QueryOperator.NOT_CONTAINS.getIndex());
                    String valueStr = valueNode.isTextual() ? value.asText():value.toString();
                    String key = StrUtil.format("#JSON#json_contains({}, {}, '$.{}')", ObjectDigital.EXTRA_COLUMN, valueStr, infoCode);
                    valueMap.put(key, criteria.numberNode(0));
                }
            } else if(value.isTextual()) {
                String key = StrUtil.format("#JSON#json_unquote(json_extract({}, '$.{}'))", ObjectDigital.EXTRA_COLUMN, infoCode);
                valueMap.put(key, value);
            } else if(value.isInt()) {
                String key = StrUtil.format("#JSON#ifnull(json_extract({}, '$.{}'), -9999)", ObjectDigital.EXTRA_COLUMN, infoCode);
                valueMap.put(key, value);
            } else if(value.isDouble()) {
                String key = StrUtil.format("#JSON#ifnull(json_extract({}, '$.{}'), -9999.99)", ObjectDigital.EXTRA_COLUMN, infoCode);
                valueMap.put(key, value);
            }
            // 处理完成后删除
            fieldNames.remove();
        }
        // 将valueMap写入criteria中
        if(MapUtil.isEmpty(valueMap)) {
            return;
        }
        for(String key:valueMap.keySet()) {
            criteria.set(key, valueMap.get(key));
        }
    }

    /**
     * 处理or条件
     * @param node
     */
    private static void processOrCond(JsonNode node) {
        if(node == null || node.isNull()) {
            return;
        }
        // 如果是数组 - 递归继续处理即可
        if(node.isArray()) {
            Iterator<JsonNode> elements = node.elements();
            while (elements.hasNext()) {
                // 递归继续处理
                JsonNode objNode = elements.next();
                processOrCond(objNode);
            }
            return;
        }
        // 如果是对象，则转换条件
        if(node.isObject()) {
            // 对象 递归从头开始处理
            ObjectNode obj = (ObjectNode) node;
            ensureExtraCond(obj);
            return;
        }
    }

    /**
     * 拼接$search条件
     * @param criteria
     */
    private static void ensureSearchCond(ObjectNode criteria) {
        if(criteria == null || criteria.isNull()) {
            return;
        }
        // 处理search
        JsonNode search = criteria.get(QueryOperator.SEARCH.getIndex());
        if(search != null && search.isTextual()) {
            String searchValue = search.asText();
            String key = StrUtil.format("#JSON#json_search({0}, 'one', {1})", ObjectDigital.EXTRA_COLUMN, searchValue);
            ObjectNode searchObj = criteria.putObject(key);
            searchObj.put(QueryOperator.NULL.getIndex(), false);
            // 处理完成后删除
            criteria.remove(QueryOperator.SEARCH.getIndex());
        }
    }

    /**
     * 处理关系条件
     * @param criteria
     */
    private static void ensureRelationCond(ObjectNode criteria) {
        if(criteria == null || criteria.isNull()) {
            return;
        }
        // 处理relationFrom
        JsonNode relationFrom = criteria.get(QueryOperator.RELATION_FROM.getIndex());
        if(relationFrom != null && relationFrom.isObject() && relationFrom.size() > 0) {
            StringBuilder inSql = new StringBuilder("(select obj_from from dt_relation where 1=1 ");
            addWhereCond(relationFrom, inSql, "graphId");
            addWhereCond(relationFrom, inSql, "graphCode");
            addWhereCond(relationFrom, inSql, "relCode");
            addWhereCond(relationFrom, inSql, "objTo");
            inSql.append(")");
            ObjectNode inObj = criteria.putObject(BaseEntity.PROP_ID);
            inObj.put(QueryOperator.IN_SQL.getIndex(), inSql.toString());
            // 处理完成后删除
            criteria.remove(QueryOperator.RELATION_FROM.getIndex());
        }
        // 处理relationTo
        JsonNode relationTo = criteria.get(QueryOperator.RELATION_TO.getIndex());
        if(relationTo != null && relationTo.isObject() && relationTo.size() > 0) {
            StringBuilder inSql = new StringBuilder("(select obj_to from dt_relation where 1=1 ");
            addWhereCond(relationTo, inSql, "graphId");
            addWhereCond(relationTo, inSql, "graphCode");
            addWhereCond(relationTo, inSql, "relCode");
            addWhereCond(relationTo, inSql, "objFrom");
            inSql.append(")");
            ObjectNode inObj = criteria.putObject(BaseEntity.PROP_ID);
            inObj.put(QueryOperator.IN_SQL.getIndex(), inSql.toString());
            // 处理完成后删除
            criteria.remove(QueryOperator.RELATION_TO.getIndex());
        }
    }

    /**
     * 添加where条件
     * @param node json对象
     * @param inSql in条件
     * @param field 属性名
     */
    private static void addWhereCond(JsonNode node, StringBuilder inSql, String field) {
        String value = (String) JsonNodeUtils.getNodeValue(node.get(field));
        if(StrUtil.isNotEmpty(value)) {
            String column = StrUtil.toUnderlineCase(field);
            inSql.append("and " + column + " = '" + value + "' ");
        }
    }
}
