package com.lantone.qc.dbanaly.facade.comsis;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.lantone.qc.dbanaly.facade.changx.CxXmlUtil;
import com.lantone.qc.dbanaly.facade.shaoyf.ShaoyfXmlUtil;
import com.lantone.qc.dbanaly.facade.taizhou.TzXmlUtil;
import com.lantone.qc.dbanaly.lt.entity.Abnormal;
import com.lantone.qc.dbanaly.lt.entity.BehospitalInfo;
import com.lantone.qc.dbanaly.lt.entity.MedicalRecord;
import com.lantone.qc.dbanaly.lt.entity.MedicalRecordContent;
import com.lantone.qc.dbanaly.lt.entity.RecordAnalyze;
import com.lantone.qc.dbanaly.lt.entity.RecordAnalyzeDetail;
import com.lantone.qc.dbanaly.lt.entity.RecordAnalyzeExample;
import com.lantone.qc.dbanaly.lt.service.impl.AbnormalServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.BehospitalInfoServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.MedicalRecordContentServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.MedicalRecordServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.ModelHospitalServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.RecordAnalyzeDetailServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.RecordAnalyzeExampleServiceImpl;
import com.lantone.qc.dbanaly.lt.service.impl.RecordAnalyzeServiceImpl;
import com.lantone.qc.pub.util.DateUtil;
import com.lantone.qc.pub.util.EncrypDES;
import com.lantone.qc.pub.util.ListUtil;
import com.lantone.qc.pub.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: rengb
 * @time: 2020/6/3 16:44
 */
@Component
public class XmlDataAnalysisFacade {
    @Autowired
    @Qualifier("medicalRecordServiceImpl")
    private MedicalRecordServiceImpl medicalRecordService;
    @Autowired
    @Qualifier("medicalRecordContentServiceImpl")
    private MedicalRecordContentServiceImpl medicalRecordContentService;
    @Autowired
    @Qualifier("recordAnalyzeServiceImpl")
    private RecordAnalyzeServiceImpl recordAnalyzeService;
    @Autowired
    @Qualifier("recordAnalyzeDetailServiceImpl")
    private RecordAnalyzeDetailServiceImpl recordAnalyzeDetailService;
    @Autowired
    @Qualifier("behospitalInfoServiceImpl")
    private BehospitalInfoServiceImpl behospitalInfoService;
    @Autowired
    @Qualifier("recordAnalyzeExampleServiceImpl")
    private RecordAnalyzeExampleServiceImpl recordAnalyzeExampleService;
    @Autowired
    @Qualifier("abnormalServiceImpl")
    private AbnormalServiceImpl abnormalService;
    @Autowired
    @Qualifier("modelHospitalServiceImpl")
    private ModelHospitalServiceImpl modelHospitalService;
    @Value("${xml-is-encryped}")
    private boolean encryped;

    public List<String> getRecTitles(long hospitalId, long modelId) {
        QueryWrapper<MedicalRecord> medicalRecordQe = new QueryWrapper<>();
        medicalRecordQe.eq("hospital_id", hospitalId);
        medicalRecordQe.eq("mode_id", modelId);
        medicalRecordQe.eq("status", 0);
        medicalRecordQe.eq("is_deleted", "N");
        medicalRecordQe.select("rec_title");
        return medicalRecordService.list(medicalRecordQe).stream().map(i -> i.getRecTitle()).filter(i -> StringUtil.isNotBlank(i)).distinct().collect(Collectors.toList());
    }

    @Transactional(transactionManager = "db1TransactionManager")
    public void analysisByRecTitle(long hospitalId, long modelId, String modeName, String recTitle, List<String> excludeRecTitles, String sex) throws Exception {
        //找出未处理的文书记录
        QueryWrapper<MedicalRecord> medicalRecordQe = new QueryWrapper<>();
        medicalRecordQe.eq("hospital_id", hospitalId);
        medicalRecordQe.eq("mode_id", modelId);
        medicalRecordQe.eq("status", 0);
        medicalRecordQe.eq("is_deleted", "N");
        medicalRecordQe.eq(StringUtil.isNotBlank(recTitle), "rec_title", recTitle);
        medicalRecordQe.notIn(ListUtil.isNotEmpty(excludeRecTitles), "rec_title", excludeRecTitles);
        medicalRecordQe.select("rec_id", "behospital_code");
        List<MedicalRecord> medicalRecordList = medicalRecordService.list(medicalRecordQe);
        if (ListUtil.isEmpty(medicalRecordList)) {
            return;
        }

        //找出文书记录所属人 组装为map，key为病历号，value为科室信息；格式：病历号 - 病历号(姓名，科室)
        QueryWrapper<BehospitalInfo> behospitalInfoQe = new QueryWrapper<>();
        behospitalInfoQe.in(
                "behospital_code",
                medicalRecordList.stream().map(i -> i.getBehospitalCode()).filter(i -> StringUtil.isNotBlank(i)).distinct().collect(Collectors.toList())
        );
        behospitalInfoQe.eq(StringUtil.isNotBlank(sex), "sex", sex);
        behospitalInfoQe.eq("hospital_id", hospitalId);
        behospitalInfoQe.eq("is_deleted", "N");
        Map<String, String> behospitalCodeDeptInfoMap =
                behospitalInfoService.list(behospitalInfoQe)
                        .stream()
                        .collect(Collectors.toMap(i -> i.getBehospitalCode(), i -> i.getBehospitalCode() + "(" + i.getName() + "," + i.getBehDeptName() + ")"));

        //对文书记录进行过滤，找出病历号正常的记录
        List<String> recIds =
                medicalRecordList
                        .stream()
                        .filter(i -> StringUtil.isNotBlank(behospitalCodeDeptInfoMap.get(i.getBehospitalCode())))
                        .map(i -> i.getRecId())
                        .collect(Collectors.toList());

        //对文书记录进行过滤，找出病历号正常的文书记录，并组装为map，key为记录id，value为病历号，格式：记录id - 病历号
        Map<String, String> recIdBehospitalCodeMap =
                medicalRecordList
                        .stream()
                        .filter(i -> StringUtil.isNotBlank(behospitalCodeDeptInfoMap.get(i.getBehospitalCode())))
                        .collect(Collectors.toMap(MedicalRecord::getRecId, i -> i.getBehospitalCode()));

        //根据文书记录id集合，分批次处理
        List<Map.Entry<Set<String>, String>> keysBehospitalCodeEntryList = Lists.newArrayList();
        int index = 0;
        List<String> queryRecIds = null;
        EncrypDES encrypDES = encryped ? new EncrypDES() : null;
        while (index <= recIds.size() - 1) {
            if (index + 1000 > recIds.size() - 1) {
                queryRecIds = recIds.subList(index, recIds.size());
            } else {
                queryRecIds = recIds.subList(index, index + 1000);
            }
            keysBehospitalCodeEntryList.addAll(getKeysBehospitalCodeEntryList(hospitalId, modelId, recTitle, sex, queryRecIds, recIdBehospitalCodeMap, encrypDES));
            if (index + 1000 > recIds.size() - 1) {
                index = recIds.size();
            } else {
                index = index + 1000;
            }
        }
        if (ListUtil.isEmpty(keysBehospitalCodeEntryList)) {
            return;
        }
        Map<Set<String>, List<String>> keysBehospitalCodesMap = keysBehospitalCodeEntryList.stream().collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

        //1、找出已经分析生成过的文书模块模板类型；2、找出相应的map_keys-模块模板类型id对应信息；3、找出相应的模块模板类型id-示例病历号对应信息
        Map<Set<String>, Long> alreadyExistsDataMap = Maps.newHashMap();//相应的map_keys-模块模板类型id对应信息
        Map<Long, Set<String>> raBecsMap = Maps.newHashMap();//相应的模块模板类型id-示例病历号对应信息
        QueryWrapper<RecordAnalyze> recordAnalyzeQe = new QueryWrapper<>();
        /*recordAnalyzeQe.eq("is_deleted", "N");//不能加这个条件，文书模块模板类型名称生成时靠历史个数累加map_type，加上后，map_type可能会重复之前出现过的*/
        recordAnalyzeQe.eq("hospital_id", hospitalId);
        recordAnalyzeQe.eq("mode_id", modelId);
        recordAnalyzeQe.eq(StringUtil.isNotBlank(recTitle), "rec_type", recTitle);
        recordAnalyzeQe.notIn(ListUtil.isNotEmpty(excludeRecTitles), "rec_type", excludeRecTitles);
        recordAnalyzeQe.like(StringUtil.isNotBlank(sex), "name", sex);
        recordAnalyzeQe.select("id");
        List<Long> recordAnalyzeIds = recordAnalyzeService.list(recordAnalyzeQe).stream().map(i -> i.getId()).collect(Collectors.toList());
        if (ListUtil.isNotEmpty(recordAnalyzeIds)) {
            QueryWrapper<RecordAnalyzeDetail> recordAnalyzeDetailQe = new QueryWrapper<>();
            recordAnalyzeDetailQe.in("record_analyze_id", recordAnalyzeIds);
            Map<Long, Set<String>> recordAnalyzeIdMapKeysMap = recordAnalyzeDetailService.list(recordAnalyzeDetailQe).stream().collect(Collectors.groupingBy(i -> i.getRecordAnalyzeId(), Collectors.mapping(i -> i.getMapKey(), Collectors.toSet())));
            recordAnalyzeIdMapKeysMap.keySet().forEach(key -> {
                alreadyExistsDataMap.put(recordAnalyzeIdMapKeysMap.get(key), key);
            });

            QueryWrapper<RecordAnalyzeExample> recordAnalyzeExampleQe = new QueryWrapper<>();
            recordAnalyzeExampleQe.in("record_analyze_id", recordAnalyzeIds);
            raBecsMap = recordAnalyzeExampleService.list(recordAnalyzeExampleQe).stream().collect(Collectors.groupingBy(i -> i.getRecordAnalyzeId(), Collectors.mapping(i -> i.getBehospitalCode(), Collectors.toSet())));
        }

        //总结出将要新增或者更新的数据
        //1、需要新增的文书模块模板类型；2、需要更新的文书模块模板类型；3、需要新增的示例病历号；
        List<RecordAnalyze> addRecordAnalyzeList = Lists.newArrayList();//需要新增的文书模块模板类型
        List<RecordAnalyze> uptRecordAnalyzeList = Lists.newArrayList();//需要更新的文书模块模板类型
        List<RecordAnalyzeExample> recordAnalyzeExampleList = Lists.newArrayList();//需要新增的示例病历号
        Date now = DateUtil.now();
        int type = alreadyExistsDataMap.size() + 1;
        Set<String> behospitalCodeDeptInfoSet = null;
        String sexAdd = "";
        if (StringUtil.isNotBlank(sex)) {
            sexAdd = sex + "-";
        }
        Long alreadyExistsRecordAnalyzeId;//已经分析生成过的文书模块模板类型id
        Set<String> exitBecs = null;
        for (Set<String> keys : keysBehospitalCodesMap.keySet()) {
            if (keys.size() == 0) {
                continue;
            }
            alreadyExistsRecordAnalyzeId = alreadyExistsDataMap.get(keys);
            behospitalCodeDeptInfoSet = keysBehospitalCodesMap.get(keys).stream().distinct().map(bec -> behospitalCodeDeptInfoMap.get(bec)).collect(Collectors.toSet());
            RecordAnalyze recordAnalyze = new RecordAnalyze();
            if (alreadyExistsRecordAnalyzeId == null) {
                recordAnalyze.setName(modeName + "-" + recTitle + "-" + sexAdd + type);
                recordAnalyze.setHospitalId(hospitalId);
                recordAnalyze.setModeId(modelId);
                recordAnalyze.setModeName(modeName);
                recordAnalyze.setRecType(recTitle);
                recordAnalyze.setBehospitalCodes(behospitalCodeDeptInfoSet.stream().collect(Collectors.joining(";")));
                recordAnalyze.setMapType(type + "");
                recordAnalyze.setMapKeys(keys.stream().collect(Collectors.joining(",")));
                recordAnalyze.setGmtCreate(now);
                recordAnalyze.setGmtModified(now);
                addRecordAnalyzeList.add(recordAnalyze);
                type++;
            } else {
                if (StringUtil.isNotBlank(recordAnalyze.getBehospitalCodes())) {
                    behospitalCodeDeptInfoSet.addAll(Sets.newHashSet(recordAnalyze.getBehospitalCodes().split(";")));
                }
                recordAnalyze.setId(alreadyExistsRecordAnalyzeId);
                recordAnalyze.setBehospitalCodes(behospitalCodeDeptInfoSet.stream().collect(Collectors.joining(";")));
                recordAnalyze.setGmtModified(now);
                uptRecordAnalyzeList.add(recordAnalyze);

                exitBecs = raBecsMap.get(alreadyExistsRecordAnalyzeId);
                for (String bhrec : keysBehospitalCodesMap.get(keys).stream().distinct().collect(Collectors.toList())) {
                    if (exitBecs == null || !exitBecs.contains(bhrec)) {
                        RecordAnalyzeExample recordAnalyzeExample = new RecordAnalyzeExample();
                        recordAnalyzeExample.setRecordAnalyzeId(alreadyExistsRecordAnalyzeId);
                        recordAnalyzeExample.setBehospitalCode(bhrec);
                        recordAnalyzeExampleList.add(recordAnalyzeExample);
                    }
                }
            }
        }

        //进行数据新增或者更新
        if (ListUtil.isNotEmpty(addRecordAnalyzeList)) {
            recordAnalyzeService.saveBatch(addRecordAnalyzeList);
            List<RecordAnalyzeDetail> recordAnalyzeDetailList = Lists.newArrayList();//需要新增的map_key
            List<Abnormal> abnormalList = Lists.newArrayList();//需要新增的异常记录
            addRecordAnalyzeList.forEach(raly -> {
                Set<String> mapKeySet = Sets.newHashSet(raly.getMapKeys().split(","));
                mapKeySet.forEach(mky -> {
                    RecordAnalyzeDetail recordAnalyzeDetail = new RecordAnalyzeDetail();
                    recordAnalyzeDetail.setRecordAnalyzeId(raly.getId());
                    recordAnalyzeDetail.setMapKey(mky);
                    recordAnalyzeDetailList.add(recordAnalyzeDetail);
                });

                keysBehospitalCodesMap.get(mapKeySet).stream().distinct().forEach(bocd -> {
                    RecordAnalyzeExample recordAnalyzeExample = new RecordAnalyzeExample();
                    recordAnalyzeExample.setRecordAnalyzeId(raly.getId());
                    recordAnalyzeExample.setBehospitalCode(bocd);
                    recordAnalyzeExampleList.add(recordAnalyzeExample);
                });

                keysBehospitalCodesMap.get(mapKeySet).forEach(bocd -> {
                    Abnormal abnormal = new Abnormal();
                    abnormal.setHospitalId(hospitalId);
                    abnormal.setBehospitalCode(bocd);
                    abnormal.setType(3);
                    abnormal.setStatus(0);
                    abnormal.setDescription(raly.getName());
                    abnormal.setGmtCreate(now);
                    abnormal.setGmtModified(now);
                    abnormalList.add(abnormal);
                });
            });
            recordAnalyzeDetailService.saveBatch(recordAnalyzeDetailList);
            abnormalService.saveBatch(abnormalList);
        }
        if (ListUtil.isNotEmpty(uptRecordAnalyzeList)) {
            recordAnalyzeService.updateBatchById(uptRecordAnalyzeList);
        }
        if (ListUtil.isNotEmpty(recordAnalyzeExampleList)) {
            recordAnalyzeExampleService.saveBatch(recordAnalyzeExampleList);
        }
    }

    //根据文书记录id集合进行处理
    private List<Map.Entry<Set<String>, String>> getKeysBehospitalCodeEntryList(long hospitalId, long modelId, String recTitle, String sex, List<String> recIds, Map<String, String> recIdBehospitalCodeMap, EncrypDES encrypDES) throws Exception {
        List<Map.Entry<Set<String>, String>> ret = Lists.newArrayList();

        QueryWrapper<MedicalRecord> medicalRecordQe = new QueryWrapper<>();
        medicalRecordQe.eq("hospital_id",hospitalId);
        medicalRecordQe.in("rec_id",recIds);
        MedicalRecord medicalRecord = new MedicalRecord();
        medicalRecord.setStatus(1);
        medicalRecordService.update(medicalRecord,medicalRecordQe);

        //根据文书记录id集合，找出文书内容，有的文书记录可能没有对应文书内容，如果文书的xml是加密过的，还需要解密
        QueryWrapper<MedicalRecordContent> medicalRecordContentQe = new QueryWrapper<>();
        medicalRecordContentQe.eq("is_deleted", "N");
        medicalRecordContentQe.eq("hospital_id", hospitalId);
        medicalRecordContentQe.in("rec_id", recIds);
        medicalRecordContentQe.select("rec_id", "xml_text");
        List<MedicalRecordContent> medicalRecordContentList = medicalRecordContentService.list(medicalRecordContentQe);
        if (ListUtil.isEmpty(medicalRecordContentList)) {
            return ret;
        }
        if (encryped) {
            medicalRecordContentList.forEach(medicalRecordContent -> {
                medicalRecordContent.setXmlText(encrypDES.decryptor(medicalRecordContent.getXmlText()));
            });
        }

        //1、解析文书记录内容得出键值对，key是从文书的xml中提取的标签集合keys，value为对应的示例病历号；
        //2、对键值对集合进行分组组装map，key为xml标签集合kesys，value为示例病历号集合；
        String xmlText, behospitalCode;
        for (MedicalRecordContent medicalRecordContent : medicalRecordContentList) {
            xmlText = medicalRecordContent.getXmlText();
            if (StringUtil.isBlank(xmlText)) {
                continue;
            }
            behospitalCode = recIdBehospitalCodeMap.get(medicalRecordContent.getRecId());
            ret.add(
                    Maps.immutableEntry(
                            getKeys(hospitalId, modelId, recTitle, sex, xmlText),
                            behospitalCode
                    )
            );
        }

        //释放内存
        medicalRecordContentList.clear();
        System.gc();

        return ret;
    }

    private Set<String> getKeys(long hospitalId, long modelId, String recTitle, String sex, String xml) throws Exception {
        Set<String> keys = new HashSet<>();
        if (hospitalId == 1) {
            keys.addAll(CxXmlUtil.firstLevelNodeValue(xml).keySet());
        } else if (hospitalId == 2) {
            keys.addAll(ShaoyfXmlUtil.getXmlToMap(xml).keySet());
        } else if (hospitalId == 3) {
            keys.addAll(TzXmlUtil.getXmlToMapForTZ(xml).keySet());
        }
        if (keys.size() > 0) {
            if (StringUtil.isNotBlank(sex)) {
                keys.add(sex);
            }

            keys.add("mode_id=" + modelId);
            if (StringUtil.isNotBlank(recTitle)) {
                keys.add("rec_title=" + recTitle);
            }
        }
        return keys;
    }

}