package com.sagacloud.dict;

import com.sagacloud.endpoints.InfoCode;
import com.sagacloud.json.JSONObject;
import com.sagacloud.json.JSONValue;
import com.sagacloud.utils.CommonUtil;
import com.sagacloud.utils.Const;
import com.sagacloud.utils.InfoCodeUtil;
import com.sagacloud.utils.objects.ObjectType;
import com.sagacloud.utils.OssUtils;
import com.sagacloud.utils.jsonHelper.infoPoint.InfoPoint;
import com.sagacloud.utils.tables.HBaseTable;
import de.jkeylockmanager.manager.KeyLockManager;
import de.jkeylockmanager.manager.KeyLockManagers;
import org.apache.commons.io.IOUtils;
import org.apache.htrace.fasterxml.jackson.databind.ObjectMapper;
import org.apache.htrace.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;

/**
 * Created by wo on 8/21/17.
 */
public class TypeToInfosMap {
    private static final Logger log = LoggerFactory.getLogger(TypeToInfosMap.class);

    final static String NOT_FOLLOWING = "notFollowing";

    static private final KeyLockManager lockManager = KeyLockManagers.newLock();


    // project, building, floor, space, eq-gen, sy-gen 和
    // 两位系统编码或四位设备编码 --> 信息点列表
    private static HashMap<String, List<InfoPoint>> cache = new HashMap<>();

    // project, building, floor, space, eq-gen, sy-gen 和
    // 两位系统编码或四位设备编码 --> Map<信息点编码 --> 信息点>
    public static HashMap<String, Map<String, InfoPoint>> typeToInfo = new HashMap<>();
    //pjId -> ctmDictId
    private static Map<String, String> pj2DictId = new HashMap<>();
    //ctmDictId -> (type -> infos)
    private static Map<String, Map<String, List<InfoPoint>>> dict2Infos = new HashMap<>();

    public static List<InfoPoint> get(String pjId, String key){
        if(!pjId.startsWith("Pj")){
            pjId = "Pj" + pjId;
        }
        syncInit(pjId);
        if(pj2DictId.get(pjId).equalsIgnoreCase(NOT_FOLLOWING)){
            return cache.get(key);
        }
        return dict2Infos.get(pj2DictId.get(pjId)).get(key);

    }

    private static void syncInit(String pjId){
        if(pj2DictId.get(pjId) == null){
            lockManager.executeLocked(pjId, () -> {
                if(pj2DictId.get(pjId) == null){
                    init(pjId);
                }
            });
        }
    }

    /**根据pjid从缓存中查询其follow的dictId，如果没有返回null
     * @param pjId
     * @return
     */
    public static String getDictId(String pjId){
        if(!pjId.startsWith("Pj")){
            pjId = "Pj" + pjId;
        }
        syncInit(pjId);
        String dictId = pj2DictId.get(pjId);
        if(dictId.equals(NOT_FOLLOWING)){
            return null;
        }
        return dictId;
    }

    public static void synCache(){
        HashMap<String, List<InfoPoint>> map = new HashMap<>();
        try {
            JSONObject json = getAllInfoPoints();
            ObjectMapper mapper = new ObjectMapper();
            TypeFactory typeFactory = mapper.getTypeFactory();
            json.keySet().forEach(key -> {
                try {
                    map.put(key, mapper.readValue(json.get(key).toString(), typeFactory.constructCollectionType(List.class, InfoPoint.class)));
                } catch (IOException e) {
                    log.error("Failed to handle key : " + key);
                }
            });
            if(!map.isEmpty()){
                cache = map;
            }
        } catch (Exception e) {
            log.error(Arrays.toString(e.getStackTrace()));
            log.error("Failed to syn TypeToInfosMap.");
        }
        synTypeToInfo();
    }

    private static void synTypeToInfo(){
        HashMap<String, Map<String, InfoPoint>> tempTypeToInfo = new HashMap<>();
        cache.keySet().forEach(key -> {
            Map<String, InfoPoint> tempMap = new HashMap<>();
            cache.get(key).forEach(infoPoint -> {
                tempMap.put(infoPoint.getInfoPointCode(), infoPoint);
            });
            tempTypeToInfo.put(key, tempMap);
        });
        typeToInfo = tempTypeToInfo;
    }
    public static String getInfoName(String obj_type, String info_id){
        String key = "";
        ObjectType ot = ObjectType.system;
        String name = "";
        try {
            if (obj_type.equals(Const.OBJ_TYPE_PROJECT)) {
                ot = ObjectType.project;
                name = typeToInfo.get(ot.name()).get(info_id).getInfoPointName();
            } else if (obj_type.equals(Const.OBJ_TYPE_BUILDING)) {
                ot = ObjectType.building;
                name = typeToInfo.get(ot.name()).get(info_id).getInfoPointName();
            }else if (obj_type.substring(0, 2).equals(Const.OBJ_TYPE_SYSTEM)) {
                String type = obj_type.substring(4);
                if(typeToInfo.get(type).containsKey(info_id))
                    name = typeToInfo.get(type).get(info_id).getInfoPointName();
                else if(typeToInfo.get(Const.SYS_GENERAL).containsKey(info_id))
                    name = typeToInfo.get(Const.SYS_GENERAL).get(info_id).getInfoPointName();
            }else if (obj_type.substring(0, 2).equals(Const.OBJ_TYPE_EQUIPMENT)) {
                String type = obj_type.substring(4);
                if(typeToInfo.get(type).containsKey(info_id))
                    name = typeToInfo.get(type).get(info_id).getInfoPointName();
                else if(typeToInfo.get(Const.EQ_GENERAL).containsKey(info_id))
                    name = typeToInfo.get(Const.EQ_GENERAL).get(info_id).getInfoPointName();
            }else if (obj_type.equals(Const.OBJ_TYPE_SPACE)) {
                ot = ObjectType.space;
                name = typeToInfo.get(ot.name()).get(info_id).getInfoPointName();
            }else if (obj_type.equals(Const.OBJ_TYPE_FLOOR)) {
                ot = ObjectType.floor;
                name = typeToInfo.get(ot.name()).get(info_id).getInfoPointName();
            }
        }
        catch (Exception ex)
        {
            log.error(obj_type + ", " + info_id + "获取失败!");
        }
        return name;
    }

    private static JSONObject getAllInfoPoints() throws Exception {
        if (OssUtils.getProperty("env").equalsIgnoreCase("dev")) {
            URL url = new URL(OssUtils.getProperty("url.data-platform") + "/infocode/query_all");
            InputStream inputStream = url.openStream();
            String allStr = IOUtils.toString(inputStream, "UTF-8");
            IOUtils.closeQuietly(inputStream);
            return (JSONObject) JSONValue.parse(allStr);
        }else{
            return (JSONObject) JSONValue.parse(new InfoCode().queryAll(null));
        }
    }
    
    public static JSONObject getEquipmentTree(String pjId) throws Exception {
        URL url = new URL(OssUtils.getProperty("url.data-platform") + "/dict/query/equipment_all?projectId="+pjId);
        InputStream inputStream = url.openStream();
        String allStr = IOUtils.toString(inputStream, "UTF-8");
        IOUtils.closeQuietly(inputStream);
        return (JSONObject) JSONValue.parse(allStr);
    }

    private static void init(String pjId){

        try {
            com.alibaba.fastjson.JSONObject criteria = new com.alibaba.fastjson.JSONObject().fluentPut("project_id", pjId.substring(2));
            com.alibaba.fastjson.JSONObject re = CommonUtil.query(false, HBaseTable.ctm_dict_follower, criteria);
            if (re.getJSONArray("Content").size() == 0) {
                pj2DictId.put(pjId, NOT_FOLLOWING);
                return;
            }
            String dictId = re.getJSONArray("Content").getJSONObject(0).getString("dict_id");
            pj2DictId.put(pjId, dictId);
            if(dict2Infos.get(dictId) != null){
                return;
            }
        } catch (Exception e) {
            log.error("", e);
        }

        EqStaticInfo eqStaticInfo = new EqStaticInfo(pjId);
        HashMap<String, List<InfoPoint>> map = new HashMap<>();
        ObjectMapper mapper = new ObjectMapper();
        TypeFactory typeFactory = mapper.getTypeFactory();
        eqStaticInfo.getCateToNameMap().keySet().forEach(cate -> {
            try {
                map.put(cate, mapper.readValue(InfoCodeUtil.getCtmDictWholeInfos(eqStaticInfo.getDictId(), cate, eqStaticInfo).toString(), typeFactory.constructCollectionType(List.class, InfoPoint.class)));
            } catch (Exception e) {
                log.error("Failed to handle cate : " + cate);
            }
        });
        dict2Infos.put(eqStaticInfo.getDictId(), map);
    }

    public static void clearPj2Dict(String pjId){
        if(!pjId.startsWith("Pj")){
            pjId = "Pj" + pjId;
        }
        pj2DictId.remove(pjId);
    }
    public static void clearDict2Infos(String dictId){
        dict2Infos.remove(dictId);
        List<String> pjIds = new ArrayList<>();
        pj2DictId.forEach((pj, dict) -> {
            if(dict.equals(dictId)){
                pjIds.add(pj);
            }
        });
        pjIds.forEach(pj -> pj2DictId.remove(pj));
    }
}
