package com.lantone.structure.ai.process;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lantone.structure.ai.model.EntityEnum;
import com.lantone.structure.ai.model.Lemma;
import com.lantone.structure.ai.model.Relation;
import com.lantone.common.util.CatalogueUtil;
import com.lantone.structure.model.entity.Negative;
import com.lantone.structure.model.entity.PD;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description:
 * @Author: HUJING
 * @Date: 2020/2/2 10:07
 */
@Component
public class EntityProcess {
    /**
     * 树形结构存储三元组关系，返回传入实体类型为节点的所有关联实体对象
     * @param aiOut
     * @param entityType
     * @return
     */
    public List<Lemma> createEntityTree(JSONObject aiOut, String entityType) {
        List<Lemma> resultLemmaList = new ArrayList<>();
        JSONObject annotation = aiOut.getJSONObject("annotation");
        JSONArray entitys = annotation.getJSONArray("T");
        JSONArray relations = annotation.getJSONArray("R");

        if (entitys == null || entitys.isEmpty()) return resultLemmaList;

        List<Lemma> allLemmaList = loadAllLemmaList(entitys);
        for (Lemma l : allLemmaList) {
            if (entityType.equals(l.getProperty())) {
                List<String> relationIds = new ArrayList<>();
                findRelationLemma(l, allLemmaList, relations, relationIds);
                resultLemmaList.add(l);
            }
        }
        return resultLemmaList;
    }

    /**
     * 查找所有实体对象
     * @param entitys
     * @return
     */
    protected List<Lemma> loadAllLemmaList(JSONArray entitys) {
        List<Lemma> allLemmaList = new ArrayList<>();
        //所有实体读取
        for (int i = 0; i < entitys.size(); i++) {
            if (StringUtils.isEmpty(entitys.get(i).toString())) {
                continue;
            }
            JSONObject entity = entitys.getJSONObject(i);
            Lemma lemma = new Lemma();
            lemma.setId(entity.getIntValue("id"));
            lemma.setText(entity.getString("value"));
            lemma.setProperty(entity.getString("name"));
            lemma.setPosition(entity.getString("start"));
            allLemmaList.add(lemma);
        }
        return allLemmaList;
    }

    /**
     * 递归查询所有子实体信息
     * @param lemma
     * @param allLemmaList
     * @param relations
     */
    private void findRelationLemma(Lemma lemma, List<Lemma> allLemmaList, JSONArray relations, List<String> relationIds) {
        List<Relation> connectEntityIdList = getConnectEntityList(lemma.getId(), relations);
        for (Lemma l : allLemmaList) {
            for (Relation relation : connectEntityIdList) {
                if (l.getId() == relation.getId()) {
                    if (!hasRelation(l.getId(), lemma.getId(), relationIds)) {
                        l.setRelationName(relation.getRelationName());

                        List<Relation> childConnectEntityIdList = getConnectEntityList(l.getId(), relations);
                        for (Lemma c_l : allLemmaList) {
                            for (Relation c_relation : childConnectEntityIdList) {
                                if (c_l.getId() == c_relation.getId()) {
                                    if (!hasRelation(c_l.getId(), l.getId(), relationIds)) {
                                        c_l.setRelationName(relation.getRelationName());
                                        l.addRelationLemmas(c_l);
                                    }
                                }
                            }
                        }
                        lemma.addRelationLemmas(l);
//                        findRelationLemma(l, allLemmaList, relations, relationIds);
                    }
                }
            }
        }
    }

    private boolean hasRelation(int a, int b, List<String> relationIds) {
        String s = "";
        if (a > b) {
            s = a + "," + b;
        } else {
            s = b + "," + a;
        }
        if (relationIds.contains(s)) {
            return true;
        }
        relationIds.add(s);
        return false;
    }

    /**
     * 处理关系抽取输出的json
     *
     * @param outputs    关系抽取输出的json
     * @param entityType 需要处理的实体类别
     * @return
     */
    public List<Map<String, String>> processJson(JSONObject outputs, String entityType) {
        List<Map<String, String>> connectEntityList = new ArrayList<>();
        Map<String, String> connectEntity = null;
        JSONObject annotation = outputs.getJSONObject("annotation");
        JSONArray entitys = annotation.getJSONArray("T");
        JSONArray relations = annotation.getJSONArray("R");
        for (int i = 0; i < entitys.size(); i++) {
            if (StringUtils.isEmpty(entitys.get(i).toString())) {
                continue;
            }
            JSONObject entity = entitys.getJSONObject(i);
            if (entityType.equals(entity.getString("name"))) {
                int id = entity.getIntValue("id");
                List<Relation> connectEntityRelationList = getConnectEntityList(id, relations);
                if (connectEntityRelationList.size() == 0) {
                    connectEntity = new HashMap<>();
                    connectEntity.put(entity.getString("name"), entity.getString("value"));
                    connectEntityList.add(connectEntity);
                } else {
                    connectEntity = getConnectEntity(connectEntityRelationList, entitys);
                    connectEntity.put(entity.getString("name"), entity.getString("value"));
                    connectEntityList.add(connectEntity);
                }
            }
        }
        return connectEntityList;
    }

    /**
     * 获取与传入实体有关系实体的id列表(List)
     *
     * @param entityId  传入实体的id
     * @param relations 关系抽取出的关系对
     * @return connectEntityIdList 有关系实体的id列表(List)
     */
    public List<Relation> getConnectEntityList(int entityId, JSONArray relations) {
        List<Relation> connectEntityList = new ArrayList<>();
        for (int i = 0; i < relations.size(); i++) {
            if (StringUtils.isEmpty(relations.get(i).toString())) {
                continue;
            }
            JSONObject relationObjs = relations.getJSONObject(i);
            if (relationObjs.getIntValue("from") == entityId) {
                Relation relation = new Relation();
                relation.setId(relationObjs.getIntValue("to"));
                relation.setFrom(relationObjs.getIntValue("from"));
                relation.setTo(relationObjs.getIntValue("to"));
                relation.setRelationName(relationObjs.getString("name"));
                connectEntityList.add(relation);
            }
            if (relationObjs.getIntValue("to") == entityId) {
                Relation relation = new Relation();
                relation.setId(relationObjs.getIntValue("from"));
                relation.setFrom(relationObjs.getIntValue("from"));
                relation.setTo(relationObjs.getIntValue("to"));
                relation.setRelationName(relationObjs.getString("name"));
                connectEntityList.add(relation);
            }
        }
        return connectEntityList;
    }

    /**
     * 查找阴性表述， 阴性词需要在实体前面
     * @param detailLemma
     * @return
     */
    protected Negative findNegative(Lemma detailLemma) {
        for (Lemma lemma : detailLemma.getRelationLemmas()) {
            if (lemma.getProperty().equals(EntityEnum.NEGATIVE.toString())) {
                if (StringUtils.isNotEmpty(lemma.getPosition()) && StringUtils.isNotEmpty(detailLemma.getPosition())) {
                    if (Integer.parseInt(detailLemma.getPosition()) > Integer.parseInt(lemma.getPosition())) {
                        Negative negative = new Negative();
                        negative.setName(lemma.getText());
                        return negative;
                    }
                }
            }
        }
        return null;
    }

    /**
     * 查找时间表述
     * @param detailLemma
     * @return
     */
    protected PD findPD(Lemma detailLemma) {
        for (Lemma lemma : detailLemma.getRelationLemmas()) {
            if (lemma.getProperty().equals(EntityEnum.TIME.toString())) {
                PD pd = new PD();
                pd.setName(lemma.getText());
                pd.setValue(lemma.getText());
                return pd;
            }
        }
        return null;
    }

    protected <T> T findT(Lemma detailLemma, T t, String entityType) throws Exception {
        for (Lemma lemma : detailLemma.getRelationLemmas()) {
            if (lemma.getProperty().equals(entityType)) {
                BeanUtils.copyProperty(t, "name", lemma.getText());
                return t;
            }
        }
        return null;
    }

    protected <T> T findFirstT(List<Lemma> ListLemma, T t) throws Exception {
        for (Lemma lemma : ListLemma) {
            BeanUtils.copyProperty(t, "name", lemma.getText());
            return t;
        }
        return null;
    }

    /**
     * 只找出现在lemma实体位置之后的实体
     * @param detailLemma
     * @param t
     * @param entityType
     * @param <T>
     * @return
     * @throws Exception
     */
    protected <T> T findTAfter(Lemma detailLemma, T t, String entityType) throws Exception {
        for (Lemma lemma : detailLemma.getRelationLemmas()) {
            if (lemma.getProperty().equals(entityType) && Integer.parseInt(detailLemma.getPosition()) < Integer.parseInt(lemma.getPosition())) {
                BeanUtils.copyProperty(t, "name", lemma.getText());
                return t;
            }
        }
        return null;
    }

//    /**
//     * 疾病名称需要映射操作
//     * @param lemma
//     * @param diag
//     */
//    protected void setDiag(Lemma lemma, Diag diag) {
//        Map<String, String> diagRedisMap = (Map<String, String>)specialStorageUtil.get(KernelConstants.CONCEPT_DIAG_HOSPITAL_REFLECT);
//        diag.setHospitalDiagName(lemma.getText());
//        diag.setName(lemma.getText());//存放医院原始疾病名称
//        diag.setName(diagRedisMap.get(lemma.getText()) == null ? "" : diagRedisMap.get(lemma.getText()));//映射到我们的标准疾病名称
//    }

    /**
     * 查找时间
     * @param detailLemma
     * @return
     */
    protected List<PD> findTime(Lemma detailLemma) {
        List<PD> timestamps = new ArrayList<>();
        String value;
        for (Lemma lemma : detailLemma.getRelationLemmas()) {
            if (lemma.getProperty().equals(EntityEnum.TIME.toString())) {
                PD pd = new PD();
                value = lemma.getText();
                String[] valUnit = new String[2];;
                if (value.trim().length() > 0) {
                    valUnit = CatalogueUtil.extractDigit(value);
                }
                pd.setValue(valUnit[0]);
                pd.setUnit(valUnit[1]);
                timestamps.add(pd);
            }
        }
        return timestamps;
    }

    /**
     * 获取实体id列表对应的所有实体类型及实体值
     *
     * @param connectEntityIdList 实体id列表
     * @param entitys             关系抽取的实体列表
     * @return entityRelationPair 实体id列表对应的所有实体类型及实体值
     */
    public Map<String, String> getConnectEntity(List<Relation> connectEntityIdList, JSONArray entitys) {
        Map<String, String> entityRelationPair = new HashMap<>();
        for (Relation connectEntityId : connectEntityIdList) {
            for (int i = 0; i < entitys.size(); i++) {
                if (StringUtils.isEmpty(entitys.get(i).toString())) {
                    continue;
                }
                JSONObject entity = entitys.getJSONObject(i);
                if (connectEntityId.getId() == entity.getIntValue("id")) {
                    if (entityRelationPair.containsKey(entity.getString("name"))) {
                        entityRelationPair.put(entity.getString("name"),
                                entityRelationPair.get(entity.getString("name")) + "," + entity.getString("value"));
                    } else {
                        entityRelationPair.put(entity.getString("name"), entity.getString("value"));
                    }
                    break;
                }
            }
        }
        return entityRelationPair;
    }

    public  <T> List<T> addEntity(JSONObject aiOut, EntityEnum entityType, Class<T> t) throws Exception {
        List<T> list = new ArrayList<>();
        List<Map<String, String>> entityList = processJson(aiOut, entityType.toString());
        for (Map<String, String> entityMap : entityList) {
            if (StringUtils.isEmpty(entityMap.get(entityType.toString()))) {
                continue;
            }
            T o = t.newInstance();
            for (String key : entityMap.keySet()) {
                if (key.equals(entityType.toString())) {
                    BeanUtils.copyProperty(o, "name", entityMap.get(key));
                } else if (key.equals(EntityEnum.NEGATIVE.toString())) {
                    Negative negative = new Negative();
                    negative.setName(StringUtils.isEmpty(entityMap.get(key)) ? "" : entityMap.get(key));
                    BeanUtils.copyProperty(o, "negative", negative);
                }
            }
            list.add(o);
        }
        return list;
    }

    protected  <T> List<T> addEntity(JSONObject aiOut, EntityEnum entityType, Class<T> t, EntityEnum[] subEntityTypes, Object[] subNames) throws Exception {
        List<T> list = new ArrayList<>();
        List<Map<String, String>> entityList = processJson(aiOut, entityType.toString());
        for (Map<String, String> entityMap : entityList) {
            if (StringUtils.isEmpty(entityMap.get(entityType.toString()))) {
                continue;
            }
            T o = t.newInstance();
            for (String key : entityMap.keySet()) {
                if (key.equals(entityType.toString())) {
                    BeanUtils.copyProperty(o, "name", entityMap.get(key));
                } else {
                    for (int i = 0; i < subEntityTypes.length; i++) {
                        if (key.equals(subEntityTypes[i].toString())) {
                            if (subNames[i] instanceof String) {
                                BeanUtils.copyProperty(o, subNames[i].toString(), StringUtils.isEmpty(entityMap.get(key)) ? "" : entityMap.get(key));
                            } else {
                                BeanUtils.copyProperty(subNames[i], "name", StringUtils.isEmpty(entityMap.get(key)) ? "" : entityMap.get(key));
                                BeanUtils.copyProperty(o, subNames[i].getClass().getSimpleName(), subNames[i]);
                            }
                        }
                    }
                }
            }
            list.add(o);
        }
        return list;
    }


    public String[] extract_digit(String value) {
        String[] res = new String[2];
        try {
            String reg_time = "([\\d]+)([\\u4e00-\\u9fa5]+)";
            Pattern pattern = Pattern.compile(reg_time);
            Matcher matcher = pattern.matcher(value);
            if (matcher.find(0)) {
                res[0] = matcher.group(1);
                res[1] = matcher.group(2);
            } else {
                res[0] = value;
                res[1] = "";
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return res;
    }
}
