package com.sagacloud.utils;


import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.uuid.Generators;
import com.google.common.base.Strings;
import com.sagacloud.dict.EqStaticInfo;
import com.sagacloud.dict.GeoStaticInfo;
import com.sagacloud.endpoints.Component;
import com.sagacloud.endpoints.item.PhysicalObject;
import com.alibaba.fastjson.*;
import com.sagacloud.utils.objects.CommonQueryCriteria;
import com.sagacloud.utils.tables.*;
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONTokener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * Created by wushengzong on 2017/4/19.
 * Altered by Jxing on 2017/4/25
 */
public class DpUtils {
    private static final int TIME_OUT = 40 * 1000;
    private static final String HOST = "101.201.234.108";
    private static final int PORT = 8081;

    private static int seq = 0;

    private static final Logger log = LoggerFactory.getLogger(DpUtils.class);

    private static Hashtable<String, Schema> schemaMap = new Hashtable<>();

    public static String errorJsonMsg(String msg) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "failure");
        jsonObj.put("ResultMsg", msg == null ? "" : msg);
        return JSON.toJSONString(jsonObj, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static JSONObject errorJsonObjectMsg(String msg) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "failure");
        jsonObj.put("ResultMsg", msg == null ? "" : msg);
        return jsonObj;
    }

    public static JSONObject successJsonResp(String msg, JSONObject content) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", msg);
        jsonObj.put("Content", content);
        return jsonObj;
    }

    public static JSONObject successJsonResp(String msg, JSONArray content) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", msg);
        jsonObj.put("Content", content);
        return jsonObj;
    }

    public static JSONObject errorJson(String msg) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "failure");
        jsonObj.put("ResultMsg", msg == null ? "" : msg);
        return jsonObj;
    }

    public static String successJsonMsg(String msg) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", msg);
        return JSON.toJSONString(jsonObj, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    //    public static String successJsonMsgWithId(String msg, String id){
//        JSONObject jsonObj = (JSONObject) JSONValue.parse("{}");
//        jsonObj.put("Result", "success");
//        jsonObj.put("id", id);
//        jsonObj.put("ResultMsg", msg);
//        return jsonObj.toString();
//    }
    public static String successJsonMsgWithId(String msg, String id, String nameKey, String nameValue) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("id", id);
        if (nameKey != null && !nameKey.equals(""))
            jsonObj.put(nameKey, nameValue);
        jsonObj.put("ResultMsg", msg);
        return JSON.toJSONString(jsonObj, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }
    public static String successJsonMsgWithErrorInfo(Map<String, String> errorInfo, String msg) {
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("Result", "success");
        jsonObj.put("ResultMsg", msg);
        if(errorInfo != null && errorInfo.size() > 0){
            JSONArray errorIds = new JSONArray();
            for(String id : errorInfo.keySet()){
                JSONObject singleError = new JSONObject();
                singleError.put("id", id);
                singleError.put("error", errorInfo.get(id));
                errorIds.add(singleError);
            }
            jsonObj.put("Error", errorIds);
        }
        return JSON.toJSONString(jsonObj, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }
    public static String validateJsonSchema(String jsonStr, String filePath) {
        Schema schema;
        if (schemaMap.contains(filePath)) {
            schema = schemaMap.get(filePath);
        } else {
            InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath);
            org.json.JSONObject rawSchema = new org.json.JSONObject(new JSONTokener(inputStream));
            schema = SchemaLoader.load(rawSchema);
            schemaMap.put(filePath, schema);
        }
//        org.json.JSONObject rawSchema = new org.json.JSONObject(new JSONTokener(inputStream));
//        Schema schema = SchemaLoader.load(rawSchema);
        try {
            schema.validate(new org.json.JSONObject(jsonStr)); // throws a ValidationException if this object is invalid
        } catch (ValidationException e) {
            return String.join(" | ", e.getAllMessages());
        }
        return "";
    }


    public static JSONObject rmField(JSONObject node) {
        String[] keys = {"Content"};
        Arrays.stream(keys).forEach(key -> {
            if (node.containsKey(key)) {
                node.remove(key);
            }
        });
        node.put("ResultMsg", "");
        return node;
    }

    public static JSONObject rmField(JSONObject node, String msg) {
        String[] keys = {"Count", "Content"};
        Arrays.stream(keys).forEach(key -> {
            if (node.containsKey(key)) {
                node.remove(key);
            }
        });
        node.put("ResultMsg", msg);
        return node;
    }

    public static String returnJSONArray(JSONArray arr, String message, Long size) {
        JSONObject ret = JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\", \"Content\":0}");
        ret.put("ResultMsg", message);
        ret.put("Count", size);
        ret.put("Content", arr);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static String returnJSONArray(JSONArray arr, String message, Long size, Long totalSize) {
        JSONObject ret = JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\", \"Content\":0}");
        ret.put("ResultMsg", message);
        ret.put("Count", size == null ? 0 : size);
        ret.put("TotalCount", totalSize == null ? 0 : totalSize);
        ret.put("Content", arr);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static String returnJSONArray(JSONArray arr, String message) {
        JSONObject ret = JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\", \"Content\":0}");
        ret.put("ResultMsg", message);
        ret.put("Count", (long) arr.size());
        ret.put("Content", arr);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }
//    public static String returnJSONArray(com.alibaba.fastjson.JSONArray arr, Long totalSize) {
//        com.alibaba.fastjson.JSONObject ret = com.alibaba.fastjson.JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\", \"Content\":0}");
//        ret.put("Count", (long) arr.size());
//        ret.put("TotalCount", totalSize);
//        ret.put("Content", arr);
//        return ret.toString();
//    }

    public static String returnJSONArray(JSONArray arr, Long totalSize) {
        JSONObject ret = JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\", \"Content\":0}");
        ret.put("ResultMsg", "");
        ret.put("Count", (long) arr.size());
        if (totalSize != null)
            ret.put("TotalCount", totalSize);
        ret.put("Content", arr);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }


    public static boolean verifyAssignedObjID(String objId) {
        for (int i = 0; i < objId.length(); ++i) {
            char c = objId.charAt(i);
            if (!((c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123))) {
                return false;
            }
        }
        return true;
    }

    public static String batchToSingle(JSONObject criteria) {
        if (criteria.get("Content") != null && criteria.getJSONArray("Content").size() > 0) {
            JSONArray content = criteria.getJSONArray("Content");
            JSONObject obj = content.getJSONObject(0);
            obj.put("Result", "success");
            return obj.toString();
        } else if (criteria.get("Error") != null && criteria.getJSONArray("Error").size() > 0) {
            JSONObject obj = criteria.getJSONArray("Error").getJSONObject(0);
            String errorMsg = obj.getString("error");
            return DpUtils.errorJsonMsg(errorMsg);
        }
        return DpUtils.errorJsonMsg("异常错误");
    }

    public static JSONObject rewriteEntity(JSONObject node) {
        node = combineId(node);
        String id = (String) node.get("id");
        if (id.length() < 2) {
            return node;
        }
        if (node.containsKey(Const.JSON_FIELD_JSON_DATA_INFOS)) {
            node.put(Const.JSON_FIELD_JSON_DATA_INFOS, JSONObject.parseObject((String) node.get(Const.JSON_FIELD_JSON_DATA_INFOS)));
            String key = "";
            String prefix = id.substring(0, 2);
            switch (prefix) {
                case "Eq":
                    key = "EquipID";
                    break;
                case "Sy":
                    key = "SysID";
                    break;
                case "Sp":
                    key = "RoomID";
                    break;
                case "Fl":
                    key = "FloorID";
                    break;
                case "Bd":
                    key = "BuildID";
                    break;
                case "Pj":
                    key = "ProjID";
                    break;
            }
            if (node.get(Const.JSON_FIELD_JSON_DATA_INFOS) != null) {
                ((JSONObject) node.get(Const.JSON_FIELD_JSON_DATA_INFOS)).put(key, id);
            }
        }

        return node;
    }

    public static JSONObject combineId(JSONObject node) {

        String projectId = (String) node.get(ProjectTable.KEY_PJ_ID);
        node.remove(ProjectTable.KEY_PJ_ID);
        EqStaticInfo eqStaticInfo = new EqStaticInfo(projectId);
        String id = "";
        if (node.containsKey(EquipTable.KEY_EQ_ID)) {
            String pre = "Eq";
            if (eqStaticInfo.getSysSet().contains(node.get(EquipTable.COL_EQ_CATE))) {
                pre = "Sy";
            }
            id = String.join("", pre, projectId, (String) node.get(EquipTable.KEY_BD_ID), (String) node.get(EquipTable.KEY_EQ_ID));
            node.remove(EquipTable.KEY_BD_ID);
            node.remove(EquipTable.KEY_EQ_ID);
            node.put("id", id);
            return node;
        }
        if (node.containsKey(SpaceTable.KEY_SPC_ID)) {
            String pre = "Sp";
            id = String.join("", pre, projectId, (String) node.get(SpaceTable.KEY_BD_ID), (String) node.get(SpaceTable.KEY_FL_ID), (String) node.get(SpaceTable.KEY_SPC_ID));
            node.remove(SpaceTable.KEY_BD_ID);
            node.remove(SpaceTable.KEY_SPC_ID);
            node.remove(SpaceTable.KEY_FL_ID);
            node.put("id", id);
            return node;
        }
        if (node.containsKey(FloorTable.KEY_FL_ID)) {
            String pre = "Fl";
            id = String.join("", pre, projectId, (String) node.get(FloorTable.KEY_BD_ID), (String) node.get(FloorTable.KEY_FL_ID));
            node.remove(FloorTable.KEY_BD_ID);
            node.remove(FloorTable.KEY_FL_ID);
            node.put("id", id);
            return node;
        }
        if (node.containsKey(BuildingTable.KEY_BD_ID)) {
            String pre = "Bd";
            id = String.join("", pre, projectId, (String) node.get(BuildingTable.KEY_BD_ID));
            node.remove(BuildingTable.KEY_BD_ID);
            node.put("id", id);
            return node;
        }

        node.put("id", "Pj" + projectId);
        return node;
    }


    // 生成uuid
    public static String generateUUID(String prefix, boolean isRemoveDash) {
//        UUID uuid = UUID.randomUUID();
        UUID uuid = Generators.timeBasedGenerator().generate();
        return String.join("", Strings.nullToEmpty(prefix), isRemoveDash ? uuid.toString().replace("-", "") : uuid.toString());
    }

    public static boolean checkUUIDWithoutDash(String prefix, String prefixAndUUID) {
        int startIndex = 0;
        if (Strings.nullToEmpty(prefixAndUUID).length() - 32 != Strings.nullToEmpty(prefix).length())
            return false;
        if (prefix != null) {
            if (prefixAndUUID.startsWith(prefix)) {
                startIndex = prefix.length();
            } else
                return false;
        }
        for (int i = startIndex; i < startIndex + 32; ++i) {
            if (!isHexChar(prefixAndUUID.charAt(i)))
                return false;
        }
        return true;
    }

    private static boolean isHexChar(char c) {
        if ((c > 47 && c < 58) || (c > 96 && c < 103))
            return true;
        return false;
    }

    private static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");

    public static boolean isValidDate(String str) {
        try {
            // 设置lenient为false. 否则SimpleDateFormat会比较宽松地验证日期，比如2007/02/29会被接受，并转换成2007/03/01
            if (str == null || str.length() != 14)
                return false;
            format.setLenient(false);
            format.parse(str);
            return true;
        } catch (ParseException e) {
            // e.printStackTrace();
            // 如果throw java.text.ParseException或者NullPointerException，就说明格式不对
            return false;
        }
    }

    public static boolean checkId(String pjId, String id) {
        try {
            if (pjId.startsWith(Const.OBJ_TYPE_PROJECT))
                pjId = pjId.substring(2);
            if(!id.startsWith("VO")) {
                if (!id.substring(2, 12).equals(pjId))
                    return false;
            }else if (!id.substring(4, 14).equals(pjId))
                return false;
            return checkId(id);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 验证id是否满足数据平台编码规则
     *
     * @param id
     * @return
     */
    public static boolean checkId(String id) {
//        return true;
        try {
            String type = null;
            String geoCode = null;

            if (id.startsWith("VO")) {
                type = id.substring(0, 4);
                geoCode = id.substring(4, 10);
            } else if (id.startsWith(Const.OBJ_TYPE_PROPERTY)) {
                return id.matches("[0-9a-zA-Z]+");
            } else {
                type = id.substring(0, 2);
                geoCode = id.substring(2, 8);
            }
//            if (!GeoStaticInfo.codeToNameMap.containsKey(geoCode))
//                return false;
            return PhysicalObject.allTypes.contains(type) && id.matches("[0-9a-zA-Z]+");
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 按照新的uuid编码规则
     *
     * @param criteriaNode 与CommonUtil中不同的是，此方法将对资产id进行改写时，obj_type为Pe
     * @param key
     * @throws Exception
     */
    public static void rewriteCriteria(JSONObject criteriaNode, String key, boolean deleteKey) throws Exception {
        if (criteriaNode.get(key) == null)
            return;
        String id = criteriaNode.get(key).toString().trim();
        String project_id = null;
        String obj_type = null;
        String obj_id = null;
        String exceptionMsg = String.join("", "Id:", id, "错误");
        if (id.startsWith(Const.OBJ_TYPE_PROJECT)) {
            project_id = id.substring(2, 12);
            obj_type = Const.OBJ_TYPE_PROJECT;
        } else if (id.startsWith(Const.OBJ_TYPE_VO)) {
            project_id = id.substring(4, 14);
            obj_type = id.substring(0, 4);
            obj_id = id.substring(14);
        } else if (id.startsWith(Const.OBJ_TYPE_PROPERTY)) {//Pe + uuid
            obj_type = Const.OBJ_TYPE_PROPERTY;
            obj_id = id.substring(2);
        } else {//建筑，楼层，空间，系统，设备，部件, 竖井
            project_id = id.substring(2, 12);
            obj_type = id.substring(0, 2);
            obj_id = id.substring(12);
        }
        if (deleteKey)
            criteriaNode.remove(key);
        criteriaNode.put(ObjectTable.KEY_PJ_ID, project_id);
        if (key.equals(Const.JSON_FIELD_FROM_ID)) {
            criteriaNode.put(RelTable.KEY_FROM_OBJ_TYPE, obj_type);
            criteriaNode.put(RelTable.KEY_FROM_OBJ_ID, obj_id);
        } else if (key.equals(Const.JSON_FIELD_TO_ID)) {
            criteriaNode.put(RelTable.KEY_TO_OBJ_TYPE, obj_type);
            criteriaNode.put(RelTable.KEY_TO_OBJ_ID, obj_id);
        } else {
            criteriaNode.put(ObjectTable.KEY_OBJ_TYPE, obj_type);
            criteriaNode.put(ObjectTable.KEY_OBJ_ID, obj_id);
        }
    }

    public static void main(String[] args) {
//        String str = "bd1234560012";
//        System.out.println(str.substring(2, str.length()));
//        System.out.println(String.join("d","ab", "dc"));

//        System.out.println(generateUUID("PEq", false));
//        System.out.println(generateUUID("PEq", true));
//        System.out.println(generateUUID("PEc", false));
//        System.out.println(generateUUID("PEc", true));
//
//        System.out.println(checkUUIDWithoutDash("PEq", "PEq7be52128c2234d679aff9f19ae665abe"));

//        System.out.println(isValidDate("20180103000000"));
//        System.out.println(isValidDate("2018013000000"));
//        System.out.println(isValidDate("20180113000000"));
//        System.out.println(isValidDate("20180133000000"));
        System.out.println(checkId("Pj1101080001"));
    }

    public static String batchReturn(boolean success, String message, JSONArray errors) {
        JSONObject ret = JSONObject.parseObject("{\"Result\":\"success\",\"ResultMsg\":\"\"}");
        ret.put("ResultMsg", message);
        ret.put("Result", success ? "success" : "failure");
        if (errors.size() != 0)
            ret.put("Error", errors);
        return JSON.toJSONString(ret, SerializerFeature.WriteMapNullValue, SerializerFeature.DisableCircularReferenceDetect);
    }

    public static void filterDetailedType(String pjId, Set<String> relResult, Set<String> typeSet) throws Exception {
        CommonQueryCriteria cqc = new CommonQueryCriteria();
        cqc.setNoInfo(true);
        cqc.setValid(null);
        JSONArray criterias = new JSONArray();
        for (String id : relResult) {
            JSONObject idObj = new JSONObject();
            idObj.put("id", id);
            criterias.add(idObj);
        }
        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        JSONArray result = CommonUtil.batchQuerySelfInfo(pjId, criterias, cqc, eqStaticInfo);
        Set<String> idSet = new HashSet<>();
        for (int i = 0; i < result.size(); ++i) {
            try {
                JSONObject singleObj = result.getJSONObject(i);
                String id = singleObj.getString("id");
                if (id.startsWith("Pe")) {
                    String type = singleObj.getString("family");
                    if (!typeSet.contains("Pe") && !typeSet.contains(type))
                        continue;
                } else if (id.startsWith("Eq") || id.startsWith("Sy") || id.startsWith("Ec")) {
                    String type = singleObj.getString("category");
                    if (!typeSet.contains(id.substring(0, 2)) && !typeSet.contains(type.substring(2)))
                        continue;
                } else if (id.startsWith("Sp")) {
                    String type = singleObj.getString("business_type");
                    if (!typeSet.contains("Sp") && !typeSet.contains(type))
                        continue;
                } else {
                    if (!(typeSet.contains(id.substring(0, 2)) || typeSet.contains(id.substring(0, 4))))
                        continue;
                }
                idSet.add(id);
            } catch (Exception ignore) {
            }
        }
        relResult.retainAll(idSet);
    }

    public static void filterMainType(Set<String> relResult, Set<String> typeSet) {
        if (typeSet.size() == 0)
            return;
        Set<String> set = new HashSet<>();
        for (String id : relResult) {
            if (id == null)
                continue;
            if (typeSet.contains(id.substring(0, 2)) || typeSet.contains(id.substring(0, 4))) {
                set.add(id);
            }
        }
        relResult.retainAll(set);
    }

    public static boolean filterDirectOnly(String directOnlyId, JSONObject relation) {
        if (directOnlyId == null || directOnlyId.length() == 0)
            return true;
        // 直接在项目下的资产
        if (directOnlyId.startsWith(Const.OBJ_TYPE_PROJECT)) {
            if (relation.get("building") == null && relation.get("floor") == null)
                return true;
            return false;
        }
        if (directOnlyId.startsWith(Const.OBJ_TYPE_BUILDING)) {
            if (relation.get("building") != null && relation.get("floor") == null) {
                return true;
            }
            return false;
        }
        return true;
    }


    public static void submitToObjectTable(String pjId, JSONArray criterias, String time, JSONArray errors) throws Exception {
        JSONArray insertArr = new JSONArray();
        for (int i = criterias.size() - 1; i > -1; --i) {
            JSONObject single = criterias.getJSONObject(i);
            String id = single.getString("id");
            String category = single.getString(getSubtypeKeyById(id));
            JSONObject insertObj = new JSONObject();
            insertObj.put("id", id);
            try {
                CommonUtil.rewriteObjectTableCriteria(pjId, insertObj, "id", true);
                insertArr.add(insertObj);
                insertObj.put(ObjectTable.COL_VALID, true);
                insertObj.put(ObjectTable.COL_SUBTYPE, category);
                insertObj.put(ObjectTable.KEY_CREATE_TIME, time);
            } catch (Exception e) {
            }
        }
        submitToObjectTable(criterias, errors, insertArr);
    }

    private static String getSubtypeKeyById(String id) {
        if (id.startsWith("Sp"))
            return Const.BUSINESS_TYPE;
        if (id.startsWith("Eq") || id.startsWith("Sy"))
            return "equipment_category";
        if(id.startsWith("Ec"))
            return Component.typeKey;
        if(id.startsWith("Cs") || id.startsWith("Tl"))
            return Const.CATEGORY;
        return "";
    }

    private static void submitToObjectTable(JSONArray criterias, JSONArray errors, JSONArray insertArr) throws Exception {
        Set<String> errorIdSet = new HashSet<>();
        try {
            JSONObject result = CommonUtil.insert(true, HBaseTable.objects, insertArr);
            if (!"success".equals(result.getString("Result"))) {
                JSONArray errorContent = result.getJSONArray("Content");
                if (errorContent == null)
                    throw new Exception();
                // 有数据未被添加
                for (int i = 0; i < errorContent.size(); ++i) {
                    JSONObject singleObj = errorContent.getJSONObject(i);
                    String id = CommonMethods.getIdByObjTabel(singleObj);
                    errorIdSet.add(id);
                }
            }
        } catch (Exception ex) {
            throw new Exception("数据添加失败");
        }
        if (errorIdSet.size() == 0)
            return;
        for (int i = criterias.size() - 1; i > -1; --i) {
            JSONObject single = criterias.getJSONObject(i);
            String id = single.getString("id");
            if (errorIdSet.contains(id)) {
                PropertyUtil.moveToError(criterias, i, errors, "id已存在", id);
            }
        }
    }


}
