package com.sagacloud.utils.relationUtils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sagacloud.broadcast.MessageHandler;
import com.sagacloud.utils.CommonMethods;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.ObjectUtil;
import com.sagacloud.utils.tables.HBaseTable;
import com.sagacloud.utils.tables.RelTable;

import java.util.*;


/**
 * Created by Xiaoyu on 2019/1/4
 */
public class RelationBuilder {
    /*
     * 1.过滤fromId,toId：1)验证是否存在 2)验证是否是true
     * 2.过滤图类型和关系类型
     * 3.过滤图实例
     * 4.根据图类型获取图实例
     * 5.insert
     *
     * */

    private String projectId;
    private List<RelationNode> data;
    private List<RelationNode> error;

    public RelationBuilder(String projectId) {
        if (projectId.startsWith(Const.OBJ_TYPE_PROJECT)) {
            this.projectId = projectId.substring(2);
        }else
            this.projectId = projectId;
    }

    public List<RelationNode> getData() {
        return data;
    }

    public void setData(List<RelationNode> data) {
        this.data = data;
    }

    private void addData(RelationNode rNode) {
        if (this.data == null) {
            data = new ArrayList<>();
        }
        data.add(rNode);
    }

    public List<RelationNode> getError() {
        return error;
    }

    public Map<String, Set<String>> getErrorMap(List<RelationNode> data, List<RelationNode> error, String... types) {
        List<RelationNode> tmp = new ArrayList<>();
        if(data != null)
            tmp.addAll(data);
        if(error != null)
            tmp.addAll(error);
        Map<String, Set<String>> result = new HashMap<>();
        if (types == null || types.length == 0 || tmp == null || tmp.size() == 0) {
            return result;
        }
        for (RelationNode rNode : tmp) {
            if (rNode.getFromId() != null) {
                for (String type : types) {
                    if (rNode.getFromId().startsWith(type)) {
                        if (!result.containsKey(rNode.getFromId())) {
                            result.put(rNode.getFromId(), new HashSet<>());
                        }
                        result.get(rNode.getFromId()).add(rNode.getToId());
                    }
                }
            }
            if (rNode.getToId() != null) {
                for (String type : types) {
                    if (rNode.getToId().startsWith(type)) {
                        if (!result.containsKey(rNode.getToId())) {
                            result.put(rNode.getToId(), new HashSet<>());
                        }
                        result.get(rNode.getToId()).add(rNode.getFromId());
                    }
                }
            }
        }
        return result;
    }

    public void addErr(RelationNode relationNode) {
        if (error == null) {
            error = new ArrayList<>();
        }
        error.add(relationNode);
    }

    public RelationBuilder addFrom2To(Map<String, String> pairs, String graphType, String relType) {
        for (String key : pairs.keySet()) {
            RelationNode rNode = new RelationNode(key, pairs.get(key), graphType, null, relType);
            addData(rNode);
        }
        return this;
    }

    public RelationBuilder addTo2From(Map<String, String> pairs, String graphType, String relType) {
        for (String key : pairs.keySet()) {
            RelationNode rNode = new RelationNode(pairs.get(key), key, graphType, null, relType);
            addData(rNode);
        }
        return this;
    }

    /**另一个接口，和addTo2From不同的是参数pairs为id -> set
     * @param map
     * @param graphType
     * @param relType
     * @return
     */
    public RelationBuilder addTo2From2(Map<String, Set<String>> map, String graphType, String relType) {
        for (String key : map.keySet()) {
            map.get(key).forEach(fId -> {
                RelationNode rNode = new RelationNode(fId, key, graphType, null, relType);
                addData(rNode);
            });

        }
        return this;
    }

    /**
     * 过滤掉不存在的fromId和toId
     *
     * @return
     */
    public RelationBuilder filterNonExistObjects() throws Exception {
        validataObject(null);
        return this;
    }

    /**
     * 过滤掉valid不是true的formId和toId
     *
     * @return
     */
    public RelationBuilder filterInvalidObjects() throws Exception {
        validataObject(true);
        return this;
    }

    /**
     * 验证图类型和关系类型是否合法
     *
     * @return
     */
    public RelationBuilder validateRelation() throws Exception {
        if (data == null || data.size() == 0) {
            return this;
        }
        try {
            Map<String, Set<String>> graphRelMap = RelationUtil.getGraphRelMap(projectId);
            List<RelationNode> tempList = new ArrayList<>();
            for (RelationNode rNode : data) {
                if (graphRelMap.containsKey(rNode.getGraphType()) && graphRelMap.get(rNode.getGraphType()).contains(rNode.getRelType())) {
                    tempList.add(rNode);
                } else {
                    addErr(rNode);
                }
            }
            data = tempList;
        } catch (Exception e) {
            throw new Exception("加载图类型、关系类型失败");
        }
        return this;
    }

    /**
     * 根据图类型添加图id
     *
     * @return
     */
    public RelationBuilder fillGraphId() throws Exception {
        if (data == null || data.size() == 0) {
            return this;
        }
        Map<String, String> graphIdMap = new HashMap<>();
        for (RelationNode rNode : data) {
            if (rNode.getGraphType() != null) {
                graphIdMap.put(rNode.getGraphType(), null);
            }
        }
        try {
            RelationUtil.batchGetGraphInstance(projectId, graphIdMap, true);
            List<RelationNode> tempList = new ArrayList<>();
            for (RelationNode rNode : data) {
                if (rNode.getGraphId() != null) {//填写的graphId是存在的
                    if (graphIdMap.containsValue(rNode.getGraphId())) {
                        tempList.add(rNode);
                    } else {
                        addErr(rNode);
                    }
                } else if (rNode.getGraphType() != null) {//未填写graphId，但是填写了graphType，自动获取graphId并填进去
                    rNode.setGraphId(graphIdMap.get(rNode.getGraphType()));
                    tempList.add(rNode);
                } else {
                    addErr(rNode);
                }
            }
        } catch (Exception e) {
            throw new Exception("获取图实例失败");
        }
        return this;
    }

    /**
     * 插入数据库
     *
     * @return
     */
    public RelationBuilder insert() throws Exception {
        if (data == null || data.size() == 0) {
            return this;
        }
        List<RelationNode> tempList = new ArrayList<>();
        Set<RelationNode> nodeSet = new HashSet<>();
        //过滤合法输入并去重
        for (RelationNode node : data) {
            if (node.getFromId() == null || node.getToId() == null || node.getGraphId() == null || node.getRelType() == null) {
                addErr(node);
            } else if (!nodeSet.contains(node)) {
                nodeSet.add(node);
                tempList.add(node);
            }
        }

        Set<String> graphIds = new HashSet<>();

        this.data = tempList;
        JSONArray insertObjs = new JSONArray();
        for (RelationNode node : nodeSet) {
            JSONObject obj = new JSONObject();
            obj.put(RelTable.KEY_PJ_ID, projectId);
            obj.put(Const.JSON_FIELD_FROM_ID, node.getFromId());
            obj.put(Const.JSON_FIELD_TO_ID, node.getToId());
            obj.put(RelTable.KEY_GRAPH_ID, node.getGraphId());
            graphIds.add(node.getGraphId());
            obj.put(RelTable.KEY_REL_TYPE, node.getRelType());
            try {
                RelationUtil.rewriteCriteria(obj, Const.JSON_FIELD_FROM_ID);
                RelationUtil.rewriteCriteria(obj, Const.JSON_FIELD_TO_ID);
                insertObjs.add(obj);
            } catch (Exception e) {
                addErr(node);
            }
        }
        if (insertObjs.size() == 0)
            return this;
        CommonUtil.insert(true, HBaseTable.rel_btw_objs, insertObjs);
        //发广播
        graphIds.forEach(gId ->
                MessageHandler.publish(String.format("{\"type\":14,\"projectId\":\"%s\",\"graphId\":\"%s\",\"description\":\"\"}", CommonMethods.getProject(projectId, true), gId)));
        return this;
    }

    private void validataObject(Boolean valid) throws Exception {
        if (data == null || data.size() == 0) {
            return;
        }
        List<RelationNode> tempData = new ArrayList<>();
        Set<String> objectIdSet = new HashSet<>();
        for (RelationNode rNode : data) {
            if (rNode.getFromId() != null) {
                objectIdSet.add(rNode.getFromId());
            }
            if (rNode.getToId() != null) {
                objectIdSet.add(rNode.getToId());
            }
        }
        boolean noException = ObjectUtil.filterValid(projectId, objectIdSet, valid, null);
        if (!noException)
            throw new Exception("数据库连接异常");
        for (RelationNode rNode : data) {
            if (objectIdSet.contains(rNode.getFromId()) && objectIdSet.contains(rNode.getToId())) {
                tempData.add(rNode);
            } else {
                addErr(rNode);
            }
        }
        data = tempData;
    }
}
