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

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
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.DoctorAdviceDoc;
import com.lantone.qc.pub.model.doc.FirstCourseRecordDoc;
import com.lantone.qc.pub.model.doc.LeaveHospitalDoc;
import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
import com.lantone.qc.pub.model.doc.consultation.ConsultationResultsDoc;
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.model.label.DrugLabel;
import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
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.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author HUJING
 * @create 2020-08-26 10:51
 * @desc 医嘱与病程记录抗生素剂量不一致
 **/
@Component
public class THR03076 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();
        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
            return;
        }

        Map<Date, String> extData = null;
        if (outputInfo.getResult().get("THR02985") != null) {
            extData = (Map<Date, String>) outputInfo.getResult().get("THR02985").get("extData");
        }

        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
                .stream()
                .filter(Objects::nonNull)
                .map(DoctorAdviceDoc::getStructureMap)
                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素") && StringUtil.isNotBlank(x.get("医嘱单次剂量")))
                .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());

        //抗生素及开医嘱时间 <抗生素名,<抗生素用量,[抗生素使用时间...]>>
        Map<String, Map<String, List<Double>>> antibioticInfo = Maps.newLinkedHashMap();
        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
        //记录同一抗生素同一天内是否开过多次，用于医嘱中需要处理的抗生素过滤（一天内同一抗生素开过多次的抗生素直接过滤）
        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
        String drugName = null, value = null, startDateStr = null;
        Date startDate = null;
        for (Map<String, String> structMap : docAdvStruct) {
            drugName = structMap.get("医嘱项目名称");
            value = structMap.get("医嘱单次剂量");
            startDateStr = structMap.get("医嘱开始时间");
//            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
            startDate = StringUtil.parseDateTime(startDateStr);
            if (extData != null && extData.containsKey(startDate) && extData.get(startDate).equals(drugName)) {
                continue;   //THR02985  医嘱有抗生素使用病程无记录，规则中没报未记录的抗生素继续走这条规则，报未记录的抗生素过滤
            }
            if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
                continue;   //一天内同一抗生素开过多次的抗生素直接过滤
            }
            collectAntibioticInfo(antibioticInfo, drugName, value, startDateStr);
        }

        //抗生素及开医嘱时间 <抗生素名,<抗生素用量,[抗生素使用时间...]>>
        Map<String, Map<String, List<Double>>> antibioticWardInfo = Maps.newLinkedHashMap();
        String dateStr = null;
        /*********************************************首程治疗计划********************************************************/
        if (firstCourseRecordDoc != null) {
            DrugLabel drugLabel = firstCourseRecordDoc.getDrugLabel();
            dateStr = firstCourseRecordDoc.getStructureMap().get("记录时间");
            if (drugLabel != null && StringUtil.isNotBlank(dateStr)) {
                List<Drug> drugs = drugLabel.getDrugs();
                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
            }
        }
        /*********************************************查房记录********************************************************/
        if (threeLevelWardDocs.size() > 0) {
            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
                if (doc.getThreeLevelWardLabel().size() == 0) {
                    continue;
                }
                dateStr = doc.getStructureMap().get("查房日期");
                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
                List<Drug> drugs = label.getDrugs();
                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
            }
        }
        /**********************************************手术记录、术后首程************************************************/
        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 -> getCourseDrugInfo(antibioticWardInfo, x.getOperationRecordLabel().getDrugs(), x.getStructureMap().get("病历日期")));
            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 -> getCourseDrugInfo(antibioticWardInfo, x.getOperationDiscussionLabel().getDrugs(), x.getStructureMap().get("记录日期")));
        }
        /*********************************************会诊结果单********************************************************/
        /*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 -> getCourseDrugInfo(antibioticWardInfo, x.getConsultationResultLabel().getDrugs(), x.getStructureMap().get("会诊日期及时间")));
        }*/
        /*********************************************出院小结********************************************************/
        if (leaveHospitalDoc != null) {
            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
            dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
                List<Drug> drugs = leaveHospitalLabel.getDrugs();
                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
            }
        }

        /**
         * 1.医嘱中开了抗生素，查房记录中没有该抗生素，则医嘱中该抗生素出现过的所有时间都会提示出来
         * 2.医嘱中开了抗生素，查房记录中有该抗生素：
         *      2.1 医嘱中该抗生素某使用量（如50），查房记录中没有该使用量（如只有100），则医嘱中该抗生素使用量出现过的所有时间都会提示出来
         *      2.2 医嘱中该抗生素某使用量（如50），查房记录中也有该使用量，对比这两个时间，若医嘱时间两天内的查房记录没有该使用量，则该医嘱时间会提示出来
         */
        StringBuffer sb = new StringBuffer();
        String drugKey = null;
        for (Map.Entry<String, Map<String, List<Double>>> ai : antibioticInfo.entrySet()) {
            drugKey = ai.getKey();
            drugKey = removeBracket(drugKey).replaceAll("[^\u4e00-\u9fa5]", "");
            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
            if (StringUtil.isNotBlank(drugStandardWord)) {
                drugKey = drugStandardWord;
            }
            if (antibioticWardInfo.containsKey(drugKey)) {
                Map<String, List<Double>> adDateValue = ai.getValue();
                Map<String, List<Double>> wardDateValue = antibioticWardInfo.get(drugKey);
                for (Map.Entry<String, List<Double>> adMap : adDateValue.entrySet()) {
                    boolean match = false;
                    String adDateStr = adMap.getKey();
                    Date adDate = StringUtil.parseDateTime(adDateStr);
                    List<Double> adUsage = adMap.getValue();
                    for (Map.Entry<String, List<Double>> wdvMap : wardDateValue.entrySet()) {
                        String wardDateStr = wdvMap.getKey();
                        Date wardDate = StringUtil.parseDateTime(wardDateStr);
                        List<Double> wardUsage = wdvMap.getValue();
                        if ((adDate.before(wardDate) && !CatalogueUtil.compareTime(adDate, wardDate, 48 * 60L))
                                || (wardDate.before(adDate) && !CatalogueUtil.compareTime(wardDate, adDate, 24 * 60L))) {
                            wardUsage.removeAll(adUsage);//比如adUsage有1.0、2.0,wardUsage中有2.0、3.0,removeAll之后wardUsage只剩3.0
                            if (wardUsage.size() == 0) {
                                match = true;
                            }
                            adDateStr = DateUtil.formatDateTime(adDate);
                            if (!match && wardUsage.size() > 0 && !sb.toString().contains(drugKey + "(" + adDateStr + ")")) {
                                infoAppend(sb, ai.getKey(), adDateStr);
                                break;
                            }
                        }
                    }
                }
            }
        }

        if (sb.toString().length() > 0) {
            status.set("-1");
            info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
        }
    }

    /**
     * 记录同一抗生素同一天内是否开过多次，用于医嘱中需要处理的抗生素过滤（一天内同一抗生素开过多次的抗生素直接过滤）
     *
     * @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);
            }
        }
    }

    private void getCourseDrugInfo(Map<String, Map<String, List<Double>>> antibioticWardInfo, List<Drug> drugs, String dateStr) {
        for (Drug drug : drugs) {
            String wardDrug = drug.getName();
            wardDrug = removeBracket(wardDrug);
            String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
            if (StringUtil.isNotBlank(drugStandardWord)) {
                wardDrug = drugStandardWord;
            }
            if (drug.getConsumption() != null) {
                String consumption = drug.getConsumption().getName();
                collectAntibioticInfo(antibioticWardInfo, wardDrug, consumption, dateStr);
            }
        }
    }

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

    /**
     * 收集抗生素各种信息
     *
     * @param antibioticInfo 抗生素使用量及所有时间
     * @param drugName       抗生素名称
     * @param value          抗生素用量
     * @param startDateStr   抗生素使用时间（医嘱开始时间或查房时间）
     */
    private void collectAntibioticInfo(Map<String, Map<String, List<Double>>> antibioticInfo, String drugName, String value, String startDateStr) {
        Map<String, List<Double>> antibioticValueList = null;
        double v = -1;
        try {
            v = Double.parseDouble(getNumber(value));
        } catch (Exception e) {
            System.out.println("THR03076:       " + drugName + ":" + value + "解析异常");
        }
        if (v < 0) {
            return;
        }
        if (v > 100) {
            v = v / 1000;
        }
        if (!antibioticInfo.containsKey(drugName)) {
            //存该抗生素使用第1个值
            antibioticValueList = Maps.newLinkedHashMap();
            antibioticValueList.put(startDateStr, Lists.newArrayList(v));
            antibioticInfo.put(drugName, antibioticValueList);
        } else {
            antibioticValueList = antibioticInfo.get(drugName);
            //存该抗生素使用时间时第n个量
            if (antibioticValueList.containsKey(startDateStr)) {
                antibioticValueList.get(startDateStr).add(v);
            } else {
                //存该抗生素使用时间时第1个量
                antibioticValueList.put(startDateStr, Lists.newArrayList(v));
            }
        }
    }

    public static String getNumber(String content) {
        String group = "";
        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|\\.\\d*[1-9]|0)";
        Pattern p = Pattern.compile(compile);
        Matcher matcher = p.matcher(content);
        if (matcher.find()) {
            group = matcher.group(0);
        }
        return group;
    }

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

}
