package com.lantone.qc.kernel.catalogue.threelevelward;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.lantone.qc.kernel.catalogue.QCCatalogue;
import com.lantone.qc.kernel.util.CatalogueUtil;
import com.lantone.qc.kernel.util.SimilarityUtil;
import com.lantone.qc.pub.model.InputInfo;
import com.lantone.qc.pub.model.OutputInfo;
import com.lantone.qc.pub.model.doc.*;
import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
import com.lantone.qc.pub.model.doc.operation.OperationDiscussionDoc;
import com.lantone.qc.pub.model.doc.operation.OperationDoc;
import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
import com.lantone.qc.pub.model.entity.Drug;
import com.lantone.qc.pub.util.DateUtil;
import com.lantone.qc.pub.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @ClassName : THR02986
 * @Description : 医嘱有激素使用病程无记录
 * 药品类型(0.普药 1.抗生素 2.激素)
 * @Author : 胡敬
 * @Date: 2020-06-23 10:43
 */
@Component
public class THR02986 extends QCCatalogue {
    @Autowired
    SimilarityUtil similarityUtil;

    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
        status.set("0");
        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
            return;
        }
        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
        if (allDoctorWradDocs.size() == 0) {
            return;
        }
        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
                .stream()
                .filter(Objects::nonNull)
                .map(DoctorAdviceDoc::getStructureMap)
                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("激素"))
                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
//                .filter(x -> StringUtil.isNotBlank(x.get("给药方式")) && x.get("给药方式").contains("静脉滴注"))
//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱频率")) && !x.get("医嘱频率").equals("ONCE"))
                .collect(Collectors.toList());

        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));

        //记录同一天内是否开过多次同一激素
        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
        //记录同一激素同一天内是否开过多次，用于医嘱中需要处理的激素过滤（一天内同一激素开过多次的激素直接过滤）
        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
        Map<Date, String> doctorAdviceDrugMap = Maps.newLinkedHashMap();
        Date startDate = null;
        for (Map<String, String> adviceDoc : docAdvStruct) {
            String drugName = adviceDoc.get("医嘱项目名称");
            String startDateStr = adviceDoc.get("医嘱开始时间");
            if (StringUtil.isNotBlank(drugName)) {
//                startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
                startDate = StringUtil.parseDateTime(startDateStr);
                if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
                    continue;   //一天内同一抗生素开过多次的抗生素直接过滤
                }
                drugName = removeBracket(drugName).replaceAll("[^\u4e00-\u9fa5]", "");
                String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
                if (StringUtil.isNotBlank(drugStandardWord)) {
                    drugName = drugStandardWord;
                }
                if (drugName.contains("甲泼尼龙") || drugName.contains("泼尼松") || drugName.contains("地塞米松") || drugName.contains("可的松")) {
                    doctorAdviceDrugMap.put(startDate, adviceDoc.get("医嘱项目名称"));
                }
//                if (!Arrays.asList(JS).contains(drugName)) {
//                     doctorAdviceDrugMap.put(startDate, adviceDoc.get("医嘱项目名称"));
//                }
            }
        }

        Map<String, Date> info = Maps.newLinkedHashMap();
        Map<String, List<Drug>> infoModel = Maps.newLinkedHashMap();
        String dateStr = null;
        //入院记录中获取信息
        /*if (beHospitalizedDoc != null) {
            Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
            getInfo(info, structureMap, "入院记录", "入院日期", "治疗计划");
        }*/
        //从首程治疗计划中获取信息
        if (firstCourseRecordDoc != null) {
            getInfo(info, firstCourseRecordDoc.getStructureMap(), "首次病程录", "记录时间", "治疗计划");
            if (firstCourseRecordDoc.getDrugLabel() != null) {
                List<Drug> drugs = firstCourseRecordDoc.getDrugLabel().getDrugs();
                dateStr = firstCourseRecordDoc.getStructureMap().get("记录时间");
                if (StringUtil.isNotBlank(dateStr)) {
                    getInfo(infoModel, dateStr, drugs);
                }
            }
        }

        //从查房记录中获取信息
        List<ThreeLevelWardDoc> wardDocs = allDoctorWradDocs
                .stream()
                .filter(x -> StringUtil.isNotBlank(x.getStructureMap().get("查房日期")) && x.getThreeLevelWardLabel().size() > 0)
                .collect(Collectors.toList());
        wardDocs.forEach(x -> getInfo(info, x.getStructureMap(), "查房记录", "查房日期", "病情记录", "治疗计划和措施"));
        wardDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("查房日期"), x.getThreeLevelWardLabel().get(x.getThreeLevelWardLabel().size() - 1).getDrugs()));

        //从手术记录中获取信息
        if (operationDocs.size() > 0) {
            //手术记录
            List<OperationRecordDoc> operationRecordDocs = operationDocs
                    .stream()
                    .map(OperationDoc::getOperationRecordDoc)
                    .filter(Objects::nonNull)
                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
                    .collect(Collectors.toList());
            operationRecordDocs.forEach(x -> getInfo(info, x.getStructureMap(), "手术记录", "病历日期", "手术经过"));
            operationRecordDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("病历日期"), x.getOperationRecordLabel().getDrugs()));
            //术后首程
            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
                    .stream()
                    .map(OperationDoc::getOperationDiscussionDoc)
                    .filter(Objects::nonNull)
                    .filter(x -> x.getOperationDiscussionLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("记录日期")))
                    .collect(Collectors.toList());
            operationDiscussionDocs.forEach(x -> getInfo(info, x.getStructureMap(), "术后首程", "记录日期", "手术经过", "治疗计划和措施"));
            operationDiscussionDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("记录日期"), x.getOperationDiscussionLabel().getDrugs()));
        }

        //从会诊记录中获取信息
        /*if (consultationDocs.size() > 0) {
            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
                    .stream()
                    .map(ConsultationDoc::getConsultationResultsDoc)
                    .filter(Objects::nonNull)
                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
                    .collect(Collectors.toList());
            consultationResultsDocs.forEach(x -> getInfo(info, x.getStructureMap(), "会诊结果单", "会诊日期及时间", "会诊意见"));
            consultationResultsDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("会诊日期及时间"), x.getConsultationResultLabel().getDrugs()));
        }*/

        //从出院小结中获取信息
        if (leaveHospitalDoc != null) {
            getInfo(info, leaveHospitalDoc.getStructureMap(), "出院小结", "出院时间", "诊治经过", "出院带药");
            if (leaveHospitalDoc.getLeaveHospitalLabel() != null) {
                List<Drug> drugs = leaveHospitalDoc.getLeaveHospitalLabel().getDrugs();
                dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
                if (StringUtil.isNotBlank(dateStr)) {
                    getInfo(info, leaveHospitalDoc.getStructureMap(), "出院小结", "出院时间", "诊治经过", "出院带药");
                    getInfo(infoModel, dateStr, drugs);
                }
            }
        }

        StringBuffer sb = new StringBuffer();
        Map<Object, Object> data = Maps.newHashMap();
        Set<String> existDrug = Sets.newHashSet();
        String infoStr = "";
        for (Map.Entry<Date, String> doctorAdviceDrug : doctorAdviceDrugMap.entrySet()) {
            Date doctorAdviceDate = doctorAdviceDrug.getKey();
            String drugs = doctorAdviceDrug.getValue();
            if (drugs.contains(" ")) {
                drugs = drugs.split(" ")[0];
            }
//            drugs = removeBracket(drugs).replaceAll("[^\u4e00-\u9fa5]", "");
            Set<String> splitDrugs = CatalogueUtil.getRegexWords(drugs, "[(（\\[][^\\[\\]()（）]+[\\]）)]")
                    .stream().filter(x -> !x.equals("合资") && !x.equals("进口") && !x.equals("国产")).collect(Collectors.toSet());
            /**********************************************先文本匹配************************************************/
            String missDrug = "";
            for (Map.Entry<String, Date> map : info.entrySet()) {
                missDrug = getMissDrug(map.getKey(), map.getValue(), doctorAdviceDate, splitDrugs, 2, missDrug, existDrug);
                //当前激素药（drugs）在info中已找到，直接跳出当前循环
                if (StringUtil.isBlank(missDrug)) {
                    break;
                }
            }
            if (StringUtil.isBlank(missDrug)) {
                continue;
            }
            /**********************************************文本匹配没有找到药，再走模型************************************************/
            boolean modelFind = false;
            for (Map.Entry<String, List<Drug>> modelMap : infoModel.entrySet()) {
                dateStr = modelMap.getKey();
                List<Drug> diags = modelMap.getValue();
                Date date = StringUtil.parseDateTime(dateStr);
                //医嘱开始时间往后2天或往前一天找药
                if ((doctorAdviceDate.before(date) && !CatalogueUtil.compareTime(doctorAdviceDate, date, 48 * 60L))
                        || (date.before(doctorAdviceDate) && !CatalogueUtil.compareTime(date, doctorAdviceDate, 24 * 60L))) {
                    for (String adDrug : splitDrugs) {
                        for (Drug courseDrug : diags) {
                            if (compareStandard(courseDrug.getName(), adDrug)) {
                                modelFind = true;
                                break;
                            }
                        }
                    }
                }
                if (modelFind) {
                    break;
                }
            }
            if (StringUtil.isNotBlank(missDrug) && !modelFind && CatalogueUtil.compareTime(doctorAdviceDate, new Date(), 48 * 60L)) {
                infoAppend(sb, drugs, DateUtil.formatDateTime(doctorAdviceDate));
                data.put(doctorAdviceDate, splitDrugs.toString().replaceAll("[\\[\\]]", ""));
            }
        }
        if (StringUtil.isNotBlank(sb.toString())) {
            this.status.set("-1");
            this.info.set("医嘱：" + sb.toString().substring(0, sb.toString().length() - 1));
            extData.set(data);
        }
    }

    /**
     * 记录同一激素同一天内是否开过多次，用于医嘱中需要处理的激素过滤（一天内同一激素开过多次的激素直接过滤）
     *
     * @param docAdvStruct
     * @param antibioticDateTimes
     */
    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
        String drugName;
        String startDateStr;
        Date startDate;
        Map<Date, Integer> antibioticDateTime;
        for (Map<String, String> structMap : docAdvStruct) {
            drugName = structMap.get("医嘱项目名称");
            startDateStr = structMap.get("医嘱开始时间");
//            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
            startDate = StringUtil.parseDateTime(startDateStr);
            if (antibioticDateTimes.containsKey(drugName)) {
                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
                if (map.containsKey(startDate)) {
                    map.put(startDate, map.get(startDate) + 1);
                } else {
                    map.put(startDate, 0);
                }
            } else {
                antibioticDateTime = Maps.newHashMap();
                antibioticDateTime.put(startDate, 0);
                antibioticDateTimes.put(drugName, antibioticDateTime);
            }
        }
    }

    /**
     * 获取各模块信息<入院记录、首次病程录、手术记录、术后首程、会诊结果单、查房记录、出院小结>
     *
     * @param structureMap
     * @param info
     */
    private void getInfo(Map<String, Date> info, Map<String, String> structureMap, String modelType, String dateKey, String... contentKey) {
        String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList(contentKey));
        String recordDateStr = structureMap.get(dateKey);
        if (StringUtil.isNotBlank(recordDateStr)) {
            Date date = StringUtil.parseDateTime(recordDateStr);
            if (StringUtil.isNotBlank(content) && date != null) {
                info.put(modelType + "->" + content, date);
            }
        }
    }

    private void getInfo(Map<String, List<Drug>> info, String dateKey, List<Drug> drugs) {
        if (info.containsKey(dateKey)) {
            info.get(dateKey).addAll(drugs);
        } else {
            info.put(dateKey, drugs);
        }
    }

    /**
     * 核心：从文本中找药
     *
     * @param content          文本
     * @param wardDate
     * @param doctorAdviceDate
     * @param drugs
     * @param days
     * @return 如果文本中找到该药，则返回空字符串
     */
    private String getMissDrug(String content, Date wardDate, Date doctorAdviceDate, Set<String> drugs, int days, String missDrug, Set<String> existDrug) {
        if ("时间不匹配".equals(missDrug)) {
            missDrug = "";//初始化缺失药物
        }
        //开医嘱时间起，昨天今天明天记录内查找药
        if ((doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L))
                || (wardDate.before(doctorAdviceDate) && !CatalogueUtil.compareTime(wardDate, doctorAdviceDate, 24 * 60L))) {
            boolean findDrug = false;
            for (String drug : drugs) {
                if (StringUtil.isBlank(drug)) {
                    continue;
                }
                if (content.contains(drug.replaceAll("[^\\u4e00-\\u9fa5]", ""))
                        || (existDrug.contains(drug) && (regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗")
                        || regexFind(content, "继续", "抗感染") || regexFind(content, "治疗", "同前")))) {
                    findDrug = true;
                    existDrug.add(drug);
                    break;
                } else {
                    missDrug = concatInfo(missDrug, drug);
                }
            }
            if (findDrug) {
                missDrug = "";//如果找到一种激素药，就把报错信息置为空
            }
        } else {
            if (StringUtil.isBlank(missDrug)) {
                missDrug = "时间不匹配";
            }
        }
        return missDrug;
    }

    private String concatInfo(String infoStr, String content) {
        if (StringUtil.isBlank(infoStr)) {
            infoStr += content;
        } else {
            if (!infoStr.contains(content)) {
                infoStr += "或" + content;
            }
        }
        return infoStr;
    }

    private boolean regexFind(String content, String... str) {
        String s = "";
        for (String word : str) {
            s += word + ".*";
        }
        s = s.substring(0, s.lastIndexOf(".*"));
        Pattern p = Pattern.compile(s);
        Matcher m = p.matcher(content);
        return m.find();
    }

    /**
     * 比较两个激素标准词是否一致
     *
     * @param firstWord
     * @param secordWord
     * @return
     */
    private boolean compareStandard(String firstWord, String secordWord) {
        if (StringUtil.isBlank(firstWord) || StringUtil.isBlank(secordWord)) {
            return false;
        }
        String drugStandardWord1 = similarityUtil.getDrugStandardWord(firstWord);
        String drugStandardWord2 = similarityUtil.getDrugStandardWord(secordWord);
        if (drugStandardWord1 == null || drugStandardWord2 == null) {
            return firstWord.equals(secordWord) || firstWord.contains(secordWord) || secordWord.contains(firstWord);
        }
        return drugStandardWord1.equals(drugStandardWord2);
    }

    /**
     * 如果文本包含中括号（[海正]美罗培南针），取括号之后的文字
     *
     * @param str
     * @return
     */
    private String removeBracket(String str) {
        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
            return str.substring(str.indexOf("]") + 1);
        }
        return str;
    }

    /**
     * 拼接提示信息
     *
     * @param sb
     * @param drugKey
     * @param date
     */
    private void infoAppend(StringBuffer sb, String drugKey, String date) {
        sb.append(drugKey).append("(").append(date).append(")").append("_");
    }

    private static final List<String> filterKey = Lists.newArrayList("静脉滴注", "静脉注射", "口服", "皮下注射", "肌注", "静脉注射(泵)",
            "膀胱持续冲洗", "静脉滴注(泵)", "膀胱冲洗", "腹腔注射", "鞘内注射", "关节腔注射", "胸腔注射", "皮内");

    /*private static final String[] JS = {
            "瑞格列奈",
            "西格列汀",
            "维格列汀",
            "伏格列波糖",
            "二甲双胍",
            "硫辛酸",
            "阿卡波糖",
            "格列齐特",
            "达格列净",
            "格列美脲",
            "阿卡波糖",
            "格列美脲",
            "格列吡嗪",
            "阿卡波糖",
            "格列吡嗪",
            "二甲双胍",
            "吡格列酮二甲双胍",
            "沙格列汀",
            "吡格列酮",
            "阿仑膦酸钠维D3",
            "吡格列酮",
            "硫辛酸",
            "格列喹酮",
            "阿仑膦酸钠",
            "那格列奈",
            "格列齐特",
            "门冬胰岛素",
            "甘精胰岛素",
            "赖脯胰岛素",
            "门冬胰岛素30",
            "甲状腺",
            "胰岛素",
            "奥曲肽",
            "赖脯胰岛素",
            "生长抑素",
            "特利加压素",
            "奥曲肽",
            "精蛋白生物合成人胰岛素",
            "谷赖胰岛素",
            "地特胰岛素",
            "生长抑素",
            "甲巯咪唑",
            "鲑降钙素",
            "赖脯胰岛素",
            "利拉鲁肽",
            "重组甘精胰岛素",
            "精蛋白锌重组人胰岛素",
            "赖脯胰岛素",
            "黄体酮",
            "地屈孕酮",
            "重组人胰岛素",
            "炔诺酮",
            "特利加压素",
            "戊酸雌二醇",
            "重组人生长激素",
            "生物合成人胰岛素",
            "米非司酮",
            "黄体酮",
            "十一酸睾酮",
            "精蛋白锌重组人胰岛素",
            "丙硫氧嘧啶",
            "黄体酮",
            "重组人胰岛素",
            "重组甘精胰岛素",
            "鲑降钙素",
            "甲羟孕酮",
            "替勃龙",
            "鲑降钙素",
            "雷洛昔芬"
    };*/
}