package com.sagacloud.endpoints.customDict;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sagacloud.broadcast.MessageHandler;
import com.sagacloud.dict.EqStaticInfo;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.DpUtils;
import com.sagacloud.utils.tables.CtmNodeTable;
import com.sagacloud.utils.tables.HBaseTable;
import org.apache.commons.collections.ListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.*;

/**
 * Created by wo on 7/22/19.
 */
@Path("custom_dict")
public class CtmNode {
    private static final Logger log = LoggerFactory.getLogger(CtmNode.class);

    @Context
    private ResourceContext resourceContext;

    @Path("eq_node/b_create")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String createEqNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        JSONArray errs = this.validate(json);
        if(errs.size() != 0){
            return  DpUtils.errorJson("").fluentPut("Error", errs).toString();
        }
        JSONArray insertObjs = json.getJSONArray("nodes");
        insertObjs.forEach(o -> {
            JSONObject node = (JSONObject)o;
            node.put(CtmNodeTable.KEY_TYPE, "eq");
            node.put(CtmNodeTable.KEY_DICT_ID, json.get(CtmNodeTable.KEY_DICT_ID));
        });
        String reStr = CommonUtil.insert(true, HBaseTable.ctm_node, insertObjs).toString();
        MessageHandler.publish(String.format("{\"type\":12,\"projectId\":\"\",\"dictId\":\"%s\",\"description\":\"\"}",dictId));
        return  reStr;
    }

    /**检验输入合法性
     * @param json
     * @return
     * @throws Exception
     */
    private JSONArray validate(JSONObject json) throws Exception {
        JSONArray errs = new JSONArray();
        String dictId = json.getString("dict_id");
        JSONArray nodes = json.getJSONArray("nodes");
        Map<String, JSONObject> majors = new HashMap<>();
        Map<String, JSONObject> sys = new HashMap<>();
        Map<String, JSONObject> eqs = new HashMap<>();
        Set<String> codes = new HashSet<>();
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(node.get("code") == null){
                node.put("error", "code不能为空");
                errs.add(node);
                return;
            }
            if(codes.contains(node.getString("code"))){
                node.put("error", "编码与其它节点重复");
                errs.add(node);
                return;
            }
            codes.add(node.getString("code"));
            if(node.get("name") == null){
                node.put("error", "name不能为空");
                errs.add(node);
                return;
            }
            //专业节点
            if(node.get("parent") == null){
                if(node.getString("code").length() != 2){
                    node.put("error", "专业节点编码长度需为2");
                    errs.add(node);
                }else {
                    majors.put(node.getString("code"), node);
                }
                return;
            }
            //系统节点
            if(node.getString("code").length() == 2){
                sys.put(node.getString("code"), node);
                return;
            }
            //设备节点
            if(node.getString("code").length() == 4){
                eqs.put(node.getString("code"), node);
                return;
            }
            //如果不是部件节点
            if(node.getString("code").length() != 6){
                node.put("error", "节点编码长度不对");
                errs.add(node);
            }


        });
        if(errs.size() != 0){
            return errs;
        }
        List<String> existedEqCodes = new ArrayList<>();
        Map<String, JSONObject> familyNodes = this.enrich(dictId, majors, sys, eqs, existedEqCodes);
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(existedEqCodes.contains(node.getString("code"))){
                node.put("error", "节点已存在");
                errs.add(node);
                return;
            }
            //专业节点
            if(node.get("parent") == null){
                return;
            }
            //系统节点
            if(node.getString("code").length() == 2){
                if(majors.get(node.getString("parent")) == null){
                    node.put("error", "parent节点不存在");
                    errs.add(node);
                }
                return;
            }
            //设备节点
            if(node.getString("code").length() == 4){
                this.validateEqAnEc(dictId, sys, node, familyNodes, errs);
                return;
            }
            //部件节点
            if(node.getString("code").length() == 6){
                this.validateEqAnEc(dictId, eqs, node, familyNodes, errs);
            }
        });
        return errs;
    }

    /**
     * 判断种族'其它（XXX）'是否存在,如果不存在，则新增该种族
     */
    private void handleXxx(String dictId, Map<String, JSONObject> familyNodes){
        if(!familyNodes.keySet().contains("XXX")){
            JSONObject node = new JSONObject().fluentPut("code", "XXX").fluentPut("name", "其它");
            familyNodes.put("XXX", node);
            JSONArray nodes  = new JSONArray().fluentAdd(node);
            try {
                this.createFamilyNodes(new JSONObject().fluentPut("dict_id", dictId).fluentPut("nodes", nodes).toString());
            } catch (Exception e) {
                log.error("", e);
            }
        }
    }

    private void validateEqAnEc(String dictId, Map<String, JSONObject> sysOrEqs, JSONObject node, Map<String, JSONObject> familyNodes, JSONArray errs){
        if(sysOrEqs.get(node.getString("parent")) == null){
            node.put("error", "parent节点不存在");
            errs.add(node);
        }
        String family = node.getString("family");
        String erStr = null;
        if(family == null || family.isEmpty()){
            //根据需求，如果family不填，自动设为'其它'种族
            family = "XXX";
            node.put("family", family);
            this.handleXxx(dictId, familyNodes);
        }else{
            if(familyNodes.get(family) == null){
                erStr = node.getString("error") == null? "" : node.getString("error") + "##";
                node.put("error", erStr + "family节点没定义");
                errs.add(node);
            }else{
                String ref = node.getString("ref");
                if(ref != null && !ref.isEmpty()){
                    EqStaticInfo eqStaticInfo = new EqStaticInfo(null);
                    if(!eqStaticInfo.getEqToFamilyMap().keySet().contains(ref)){
                        erStr = node.getString("error") == null? "" : node.getString("error") + "##";
                        node.put("error", erStr + "ref值在基准字典不存在");
                        errs.add(node);
                    }else{
                        if(!familyNodes.get(family).getString("ref").equals(eqStaticInfo.getEqToFamilyMap().get(ref))){
                            erStr = node.getString("error") == null? "" : node.getString("error") + "##";
                            node.put("error", erStr + "ref值与family逻辑上不一致");
                            errs.add(node);
                        }
                    }
                }
            }

        }
    }

    /**补足数据库中已有节点并返回种族节点
     * @param dictId
     * @param majors
     * @param sys
     * @param eqs
     * @throws Exception
     */
    private Map<String, JSONObject> enrich(String dictId, Map<String, JSONObject> majors, Map<String, JSONObject> sys, Map<String, JSONObject> eqs, List<String> existedCodes) throws Exception {
        JSONArray nodes = this.queryAllNodes(dictId);
        Map<String, JSONObject> familyNodes = new HashMap<>();
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(!node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                familyNodes.put(node.getString(CtmNodeTable.KEY_CODE), node);
                return;
            }

            existedCodes.add(node.getString("code"));

            //专业节点
            if(node.get("parent") == null){
                majors.put(node.getString("code"), node);
                return;
            }
            //系统节点
            if(node.getString("code").length() == 2){
                sys.put(node.getString("code"), node);
                return;
            }
            //设备节点
            if(node.getString("code").length() == 4){
                eqs.put(node.getString("code"), node);
                return;
            }
        });
        return familyNodes;
    }

    private JSONArray queryAllNodes(String dictId) throws Exception {
        JSONObject cri = new JSONObject().fluentPut(CtmNodeTable.KEY_DICT_ID, dictId);
        return CommonUtil.query(false, HBaseTable.ctm_node, cri).getJSONArray("Content");
    }

    @Path("eq_node/b_delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String delEqNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        if(json.getBoolean("force_delete") != null && json.getBoolean("force_delete")){
            this.forceDel(json);
            return DpUtils.successJsonMsg("");
        }
        JSONArray errs = this.validateDel(json);
        if(errs.size() != 0){
            return  DpUtils.errorJson("").fluentPut("Error", errs).toString();
        }
        this.del(dictId, "eq", json.getJSONArray("nodes").toJavaList(String.class));
        MessageHandler.publish(String.format("{\"type\":12,\"projectId\":\"\",\"dictId\":\"%s\",\"description\":\"\"}",dictId));
        return DpUtils.successJsonMsg("");
    }

    /**生成节点父子映射map
     * @param dictId
     * @return
     * @throws Exception
     */
    private Map<String, List<String>> getP2cs(String dictId) throws Exception {
        JSONArray allNodes = this.queryAllNodes(dictId);
        //parent to children map
        Map<String, List<String>> p2cs = new HashMap<>();
        allNodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(!node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                return;
            }
            //专业节点
            if(node.get(CtmNodeTable.COL_PARENT) == null){
                return;
            }
            String pCode = node.getString(CtmNodeTable.COL_PARENT);
            if(p2cs.get(pCode) == null){
                p2cs.put(pCode, new ArrayList<>());
            }
            p2cs.get(pCode).add(node.getString(CtmNodeTable.KEY_CODE));
        });
        return p2cs;
    }

    /**检验删除是否会有冲突
     * @param json
     * @return
     * @throws Exception
     */
    private JSONArray validateDel(JSONObject json) throws Exception {
        String dictId = json.getString("dict_id");
        Map<String, List<String>> p2cs = this.getP2cs(dictId);
        JSONArray errs = new JSONArray();
        List<String> dels = json.getJSONArray("nodes").toJavaList(String.class);
        dels.forEach(str -> {
            if(p2cs.get(str) == null){
                return;
            }
            List<String> reLst = ListUtils.subtract(p2cs.get(str), dels);
            if(!reLst.isEmpty()){
                errs.add(new JSONObject().fluentPut("code",str).fluentPut("error", "存在子节点：" + Arrays.toString(reLst.toArray())));
            }
        });
        return errs;
    }

    /**强制删除，包括相应子节点
     * @param json
     * @throws Exception
     */
    private void forceDel(JSONObject json) throws Exception {
        String dictId = json.getString("dict_id");
        Map<String, List<String>> p2cs = this.getP2cs(dictId);
        Queue<String> queue = new ArrayDeque<>();
        queue.addAll(json.getJSONArray("nodes").toJavaList(String.class));
        List<String> toDels = new ArrayList<>();
        String curCode;
        while((curCode = queue.poll()) != null){
            toDels.add(curCode);
            if(p2cs.get(curCode) != null){
                queue.addAll(p2cs.get(curCode));
            }
        }
        this.del(dictId, "eq", toDels);
    }

    /**删除
     * @param dictId
     * @param type
     * @param codes
     * @throws Exception
     */
    private void del(String dictId, String type, List<String> codes) throws Exception {
        JSONArray cris = new JSONArray();
        codes.forEach(code -> {
            cris.add(new JSONObject().fluentPut(CtmNodeTable.KEY_DICT_ID, dictId).fluentPut(CtmNodeTable.KEY_TYPE, type).fluentPut(CtmNodeTable.KEY_CODE, code));
        });
        CommonUtil.delete(true, HBaseTable.ctm_node, cris);
    }

    @Path("eq_node/query_all")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryAllEqNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        JSONArray allNodes = this.queryAllNodes(dictId);
        JSONArray content = new JSONArray();
        if(json.getBoolean("show_as_tree") == null || !json.getBoolean("show_as_tree")){
            allNodes.forEach(o -> {
                JSONObject node = (JSONObject) o;
                if(node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                    content.add(node);
                }
            });
            return new JSONObject().fluentPut("Result", "success").fluentPut("Count", content.size()).fluentPut("Content", content).fluentPut("ResultMsg", "").toString();
        }
        Map<String, JSONObject> code2Node = new HashMap<>();
        List majors = new ArrayList();
        allNodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(!node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                return;
            }
            //专业节点
            if(node.get(CtmNodeTable.COL_PARENT) == null){
                majors.add(node);
            }
            code2Node.put(node.getString(CtmNodeTable.KEY_CODE), node);
        });
        code2Node.forEach((code, node) -> {
            String pCode = node.getString(CtmNodeTable.COL_PARENT);
            if(pCode != null){
                JSONObject parent = code2Node.get(pCode);
                if(parent.get("content") == null){
                    parent.put("content", new JSONArray());
                }
                parent.getJSONArray("content").add(node);
            }

        });
        return new JSONObject().fluentPut("Result", "success").fluentPut("Count", code2Node.size()).fluentPut("Content", majors).fluentPut("ResultMsg", "").toString();
    }

    //************  -- 以下为资产节点相关接口--   ****************

    @Path("family_node/b_create")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String createFamilyNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        JSONArray errs = this.validateCreateFamily(json);
        if(errs.size() != 0){
            return  DpUtils.errorJson("").fluentPut("Error", errs).toString();
        }
        JSONArray insertObjs = json.getJSONArray("nodes");
        insertObjs.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(node.get(CtmNodeTable.COL_PARENT) != null){
                node.put(CtmNodeTable.COL_PARENT, node.get(CtmNodeTable.COL_PARENT).toString());
            }
            node.put(CtmNodeTable.KEY_TYPE, "family");
            node.put(CtmNodeTable.KEY_DICT_ID, json.get(CtmNodeTable.KEY_DICT_ID));
        });
        String reStr = CommonUtil.insert(true, HBaseTable.ctm_node, insertObjs).toString();
        MessageHandler.publish(String.format("{\"type\":12,\"projectId\":\"\",\"dictId\":\"%s\",\"description\":\"\"}",dictId));
        return reStr;
    }

    /**检验输入合法性
     * @param json
     * @return
     * @throws Exception
     */
    private JSONArray validateCreateFamily(JSONObject json) throws Exception {
        JSONArray errs = new JSONArray();
        String dictId = json.getString("dict_id");
        JSONArray nodes = json.getJSONArray("nodes");
        List<String> allCodes = new ArrayList<>();
        Set<String> codes = new HashSet<>();
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(node.get("code") == null){
                node.put("error", "code不能为空");
                errs.add(node);
                return;
            }
            if(codes.contains(node.getString("code"))){
                node.put("error", "编码与其它节点重复");
                errs.add(node);
                return;
            }
            codes.add(node.getString("code"));
            if(node.get("name") == null){
                node.put("error", "name不能为空");
                errs.add(node);
                return;
            }
            allCodes.add(node.getString("code"));
        });
        if(errs.size() != 0){
            return errs;
        }
        List<String> existedCodes = this.queryExistedFamily(dictId);
        allCodes.addAll(existedCodes);
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            //专业节点
            if(node.get("parent") == null){
                return;
            }
            List<String> parents = node.getJSONArray("parent").toJavaList(String.class);
            List<String> reLst = ListUtils.subtract(parents, allCodes);
            if(!reLst.isEmpty()){
                node.put("error", String.format("parent节点: %s 不存在", Arrays.toString(reLst.toArray())));
                errs.add(node);
                return;
            }
            if(existedCodes.contains(node.get("code"))){
                node.put("error", "该资产类已存在");
                errs.add(node);
            }
        });
        return errs;
    }

    private List<String> queryExistedFamily(String dictId) throws Exception {
        JSONArray nodes = this.queryAllNodes(dictId);
        List<String> codes = new ArrayList<>();
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                return;
            }
            codes.add(node.getString(CtmNodeTable.KEY_CODE));
        });
        return codes;
    }

    @Path("family_node/query_all")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String queryAllFamilyNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        JSONArray allNodes = this.queryAllNodes(dictId);
        JSONArray content = new JSONArray();
        /*if(json.getBoolean("show_as_tree") == null || !json.getBoolean("show_as_tree")){
            allNodes.forEach(o -> {
                JSONObject node = (JSONObject) o;
                if(node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("family")){
                    if (node.get(CtmNodeTable.COL_PARENT) != null){
                        node.put(CtmNodeTable.COL_PARENT, node.getJSONArray(CtmNodeTable.COL_PARENT));
                    }
                    content.add(node);
                }
            });
            return new JSONObject().fluentPut("Result", "success").fluentPut("Count", content.size()).fluentPut("Content", content).fluentPut("ResultMsg", "").toString();
        }*/
        Map<String, JSONObject> code2Node = new HashMap<>();
        allNodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(!node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("family")){
                return;
            }
            code2Node.put(node.getString(CtmNodeTable.KEY_CODE), node);
        });
        code2Node.forEach((code, node) -> {
            if (node.get(CtmNodeTable.COL_PARENT) == null){
                return;
            }
            node.put(CtmNodeTable.COL_PARENT, node.getJSONArray(CtmNodeTable.COL_PARENT));
            node.getJSONArray(CtmNodeTable.COL_PARENT).toJavaList(String.class).forEach(c -> {
                JSONObject parent = code2Node.get(c);
                if(parent.get("children") == null){
                    parent.put("children", new JSONArray());
                }
                parent.getJSONArray("children").add(node.getString(CtmNodeTable.KEY_CODE));
            });
        });
        return new JSONObject().fluentPut("Result", "success").fluentPut("Count", code2Node.size()).fluentPut("Content", code2Node.values()).fluentPut("ResultMsg", "").toString();
    }

    @Path("family_node/b_delete")
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    public String delFamilyNodes(String jsonStr) throws Exception {
        JSONObject json = JSONObject.parseObject(jsonStr);
        String dictId = json.getString("dict_id");
        if(!resourceContext.getResource(CtmDict.class).isExisted(dictId)){
            return DpUtils.errorJsonMsg("dict_id不存在!");
        }
        List<String> codes = json.getJSONArray("nodes").toJavaList(String.class);
        this.del(dictId, "family", codes);
        JSONArray nodes = this.queryAllNodes(dictId);
        nodes.forEach(o -> {
            JSONObject node = (JSONObject)o;
            if(node.getString(CtmNodeTable.KEY_TYPE).equalsIgnoreCase("eq")){
                return;
            }
            if(node.get(CtmNodeTable.COL_PARENT) != null){
                List<String> reLst = ListUtils.subtract(node.getJSONArray(CtmNodeTable.COL_PARENT).toJavaList(String.class), codes);
                if(node.getJSONArray(CtmNodeTable.COL_PARENT).size() > reLst.size()){
                    JSONObject cri = new JSONObject();
                    cri.fluentPut(CtmNodeTable.KEY_DICT_ID, dictId).fluentPut(CtmNodeTable.KEY_TYPE, node.get(CtmNodeTable.KEY_TYPE)).fluentPut(CtmNodeTable.KEY_CODE, node.get(CtmNodeTable.KEY_CODE));
                    JSONObject set = new JSONObject();
                    JSONArray v = new JSONArray();
                    reLst.forEach(c -> v.add(c));
                    set.fluentPut(CtmNodeTable.COL_PARENT, v.toString());
                    try {
                        CommonUtil.update(false, HBaseTable.ctm_node, cri, set);
                    } catch (Exception e) {
                        log.error("", e);
                    }
                }
            }
        });
        MessageHandler.publish(String.format("{\"type\":12,\"projectId\":\"\",\"dictId\":\"%s\",\"description\":\"\"}",dictId));
        return DpUtils.successJsonMsg("");
    }

}
