package com.supervision.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.supervision.model.*;
import com.supervision.model.Process;
import com.supervision.pojo.vo.*;
import com.supervision.service.*;
import com.supervision.vo.ask.TreatmentPlanRecordVo;
import com.supervision.vo.manage.DiseaseTreatmentPlanResVo;
import com.supervision.vo.result.AncillaryRecordByResultDAO;
import com.supervision.vo.result.PhysicalRecordByResultDAO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class AskDiagnosisResultServiceImpl implements AskDiagnosisResultService {

    private final DiagnosisPrimaryService diagnosisPrimaryService;

    private final AskPatientAnswerService askPatientAnswerService;

    private final MedicalRecService medicalRecService;

    private final DiagnosisQaRecordService diagnosisQaRecordService;

    private final DiagnosisPhysicalRecordService diagnosisPhysicalRecordService;

    private final DiagnosisAncillaryRecordService diagnosisAncillaryRecordService;

    private final ProcessService processService;

    private final PatientService patientService;

    private final DiseaseService diseaseService;

    private final TreatmentPlanRecordService treatmentPlanRecordService;

    private final DiseaseTreatmentPlanService diseaseTreatmentPlanService;

    private final DiseaseTreatmentPlanDrugService diseaseTreatmentPlanDrugService;


    @Override
    public DiagnosisResultResVO queryDiagnosisResult(String processId) {
        DiagnosisResultResVO diagnosisResultResVO = new DiagnosisResultResVO();
        diagnosisResultResVO.setId(processId);
        Process process = processService.getById(processId);
        MedicalRec medicalRec = medicalRecService.getById(process.getMedicalRecId());
        List<Disease> diseasesList = diseaseService.listDiseaseWithType(medicalRec.getDiseaseId());
        // 1.生成预期诊断结果
        diagnosisResultResVO.setExpertDiagnosisResult(creatExpertDiagnosisResult(medicalRec, diseasesList, processId));
        // 2.生成初步诊断依据
        diagnosisResultResVO.setBasisPrimaryResultResVO(createBasisPrimaryResult(medicalRec, process));
        // 3.生成证实诊断依据
        diagnosisResultResVO.setBasisConfirmResultResVO(createBasisConfirmResult(medicalRec, processId));
        // 4.生成鉴别诊断依据
        diagnosisResultResVO.setBasisIdentificationResult(createBasisIdentificationResult(medicalRec, processId));
        // 5.全面检查-(暂无相关数据) 后面再看
        diagnosisResultResVO.setFullCheck(null);
        // 6.处置方案
        diagnosisResultResVO.setDealPlan(createDealPlan(process.getDiseaseId(), processId));
        return diagnosisResultResVO;
    }

    private DealPlanResVO createDealPlan(String diseaseId, String processId) {

        DealPlanResVO dealPlanResVO = new DealPlanResVO();

        // 1. 查询处置计划记录
        List<? extends TreatmentPlanRecord> treatmentPlanRecordList = treatmentPlanRecordService.queryByProcessId(processId);
        if (CollectionUtil.isEmpty(treatmentPlanRecordList)) {
            return dealPlanResVO;
        }

        // 2. 查询出疾病处置计划
        List<DiseaseTreatmentPlanResVo> diseaseTreatmentPlanResVos = diseaseTreatmentPlanService.queryListByDiseaseId(diseaseId);
        List<String> planIds = diseaseTreatmentPlanResVos.stream().map(DiseaseTreatmentPlanResVo::getPlanId).distinct().collect(Collectors.toList());

        // 3. 查出疾病处置计划用药
        List<DiseaseTreatmentPlanDrug> diseaseTreatmentPlanDrugList = diseaseTreatmentPlanDrugService.lambdaQuery().eq(DiseaseTreatmentPlanDrug::getDiseaseId, diseaseId).list();
        List<String> drugIds = diseaseTreatmentPlanDrugList.stream().map(DiseaseTreatmentPlanDrug::getDrugId).distinct().collect(Collectors.toList());

        // 4. 组装数据
        ArrayList<TreatmentPlanRecordVo> drugTreatmentPlanResults = new ArrayList<>();
        ArrayList<TreatmentPlanRecordVo> otherTreatmentPlanResults = new ArrayList<>();
        for (TreatmentPlanRecord treatmentPlanRecord : treatmentPlanRecordList) {
            TreatmentPlanRecordVo bean = BeanUtil.toBean(treatmentPlanRecord, TreatmentPlanRecordVo.class);
            boolean flag = StrUtil.isNotEmpty(bean.getDrugId()) ? drugIds.contains(bean.getDrugId()) : planIds.contains(bean.getTreatmentPlanId());
            bean.setFlag(flag ? 1 : 0);
            if (StrUtil.isNotEmpty(bean.getDrugId())) {
                drugTreatmentPlanResults.add(bean);
            } else {
                otherTreatmentPlanResults.add(bean);
            }

        }
        dealPlanResVO.setUserTreatmentPlanType(CollectionUtil.getFirst(treatmentPlanRecordList).getDisposalMethod());
        dealPlanResVO.setDrugTreatmentPlan(drugTreatmentPlanResults);
        dealPlanResVO.setOtherTreatmentPlan(otherTreatmentPlanResults.stream().sorted(
                Comparator.comparing(TreatmentPlanRecord::getDisposalPlan)).collect(Collectors.toList()));
        return dealPlanResVO;

    }

    private BasisIdentificationResultResVO createBasisIdentificationResult(MedicalRec medicalRec, String processId) {
        BasisIdentificationResultResVO basisIdentificationResultResVO = new BasisIdentificationResultResVO();
        // 鉴别诊断依据,来自于medicalRec的DifferentialDiagnosisCriteria
        basisIdentificationResultResVO.setIdentificationDiagnosis(medicalRec.getDifferentialDiagnosisCriteria());
        // 2.2 获取体格检查
        List<PhysicalRecordByResultDAO> physicalRecordList = diagnosisPhysicalRecordService.queryPhysicalResultByProcessId(processId);
        List<BasisDiagnosisNodeResVO> nodeList = physicalRecordList.stream()
                .filter(distinctPredicateNotNull(PhysicalRecordByResultDAO::getToolLocationName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setType(1);
                    nodeResVO.setRecordName(e.getToolLocationName());
                    nodeResVO.setRecordId(e.getRecordId());
                    // 如果是必做的,则是正确
                    nodeResVO.setCorrect(Objects.equals(e.getBasisIdentification(), e.getRecordBasisIdentificationFlag()) ? 1 : 0);
                    return nodeResVO;
                }).collect(Collectors.toList());
        // 2.3 获取辅助检查
        List<AncillaryRecordByResultDAO> ancillaryRecordList = diagnosisAncillaryRecordService.queryAncillaryResultByProcessId(processId);
        List<BasisDiagnosisNodeResVO> collect = ancillaryRecordList.stream()
                .filter(distinctPredicateNotNull(AncillaryRecordByResultDAO::getItemName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setCorrect(Objects.equals(e.getBasisIdentification(), e.getRecordBasisIdentificationFlag()) ? 1 : 0);
                    nodeResVO.setRecordId(e.getRecordId());
                    nodeResVO.setRecordName(e.getItemName());
                    nodeResVO.setType(2);
                    return nodeResVO;
                }).collect(Collectors.toList());
        nodeList.addAll(collect);

        basisIdentificationResultResVO.setNodeList(nodeList);
        return basisIdentificationResultResVO;
    }

    private BasisConfirmResultResVO createBasisConfirmResult(MedicalRec medicalRec, String processId) {
        BasisConfirmResultResVO basisConfirmResultResVO = new BasisConfirmResultResVO();
        // 证实诊断依据,来自于medicalRec的ConfirmDiagnosisCriteria
        basisConfirmResultResVO.setConfirmingDiagnosis(medicalRec.getConfirmDiagnosisCriteria());
        // 2.2 获取体格检查的证实诊断依据
        List<PhysicalRecordByResultDAO> physicalRecordList = diagnosisPhysicalRecordService.queryPhysicalResultByProcessId(processId);
        List<BasisDiagnosisNodeResVO> nodeList = physicalRecordList.stream()
                .filter(distinctPredicateNotNull(PhysicalRecordByResultDAO::getToolLocationName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setType(1);
                    nodeResVO.setRecordName(e.getToolLocationName());
                    nodeResVO.setRecordId(e.getRecordId());
                    nodeResVO.setCorrect(Objects.equals(e.getBasisConfirm(), e.getRecordBasisConfirmFlag()) ? 1 : 0);
                    return nodeResVO;
                }).collect(Collectors.toList());

        // 2.3 获取辅助检查
        List<AncillaryRecordByResultDAO> ancillaryRecordList = diagnosisAncillaryRecordService.queryAncillaryResultByProcessId(processId);
        List<BasisDiagnosisNodeResVO> collect = ancillaryRecordList.stream()
                .filter(distinctPredicateNotNull(AncillaryRecordByResultDAO::getItemName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setCorrect(Objects.equals(e.getBasisConfirm(), e.getRecordBasisConfirmFlag()) ? 1 : 0);
                    nodeResVO.setRecordId(e.getRecordId());
                    nodeResVO.setRecordName(e.getItemName());
                    nodeResVO.setType(2);
                    return nodeResVO;
                }).collect(Collectors.toList());
        nodeList.addAll(collect);

        basisConfirmResultResVO.setNodeList(nodeList);
        return basisConfirmResultResVO;
    }

    private BasisPrimaryResultResVO createBasisPrimaryResult( MedicalRec medicalRec, Process process) {
        BasisPrimaryResultResVO basisPrimaryResultResVO = new BasisPrimaryResultResVO();
        // 初步诊断依据 vp_medical_rec的primarily_diagnosis_criteria
        basisPrimaryResultResVO.setPreliminaryDiagnosis(medicalRec.getPrimarilyDiagnosisCriteria());
        // 根据record记录寻找符合初步诊断依据的项目
        // 2.1 首先获取对话
        List<BasisDiagnosisNodeResVO> basisDiagnosisNodeResVOS = new ArrayList<>();
        List<DiagnosisQaRecord> qaRecordList = diagnosisQaRecordService.lambdaQuery().eq(DiagnosisQaRecord::getProcessId, process.getId()).list();
        if (CollectionUtil.isNotEmpty(qaRecordList)) {
            List<AskPatientAnswer> list = askPatientAnswerService.lambdaQuery()
                    .eq(AskPatientAnswer::getMedicalId, medicalRec.getId()).list();
            // 获取配置了答案的项目(配置了答案的,认为是正确,没有配置答案的,走默认回答的,不标记为以正确)
            Set<String> requireCheckIdSet = list.stream().map(AskPatientAnswer::getId).collect(Collectors.toSet());
            List<BasisDiagnosisNodeResVO> qaList = qaRecordList.stream().map(e -> {
                BasisDiagnosisNodeResVO node = new BasisDiagnosisNodeResVO();
                node.setType(0);
                node.setRecordName(e.getQuestion());
                node.setRecordId(e.getId());
                node.setCorrect(requireCheckIdSet.contains(e.getAnswerId()) ? 1 : 0);
                return node;

            }).collect(Collectors.toList());
            basisDiagnosisNodeResVOS.addAll(qaList);
        }
        // 2.2 获取体格检查
        List<PhysicalRecordByResultDAO> physicalRecordList = diagnosisPhysicalRecordService.queryPhysicalResultByProcessId(process.getId());
        List<BasisDiagnosisNodeResVO> physicalList = physicalRecordList.stream()
                .filter(distinctPredicateNotNull(PhysicalRecordByResultDAO::getToolLocationName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setType(1);
                    nodeResVO.setRecordName(e.getToolLocationName());
                    nodeResVO.setRecordId(e.getRecordId());
                    // 如果是必做的,则是正确
                    nodeResVO.setCorrect(null == e.getRequireCheck() ? Integer.valueOf(0) : e.getRequireCheck());
                    return nodeResVO;
                }).collect(Collectors.toList());
        basisDiagnosisNodeResVOS.addAll(physicalList);
        // 2.3 获取辅助检查
        List<AncillaryRecordByResultDAO> ancillaryRecordList = diagnosisAncillaryRecordService.queryAncillaryResultByProcessId(process.getId());
        List<BasisDiagnosisNodeResVO> collect = ancillaryRecordList.stream()
                .filter(distinctPredicateNotNull(AncillaryRecordByResultDAO::getItemName)).map(e -> {
                    BasisDiagnosisNodeResVO nodeResVO = new BasisDiagnosisNodeResVO();
                    nodeResVO.setCorrect(null == e.getRequireCheck() ? Integer.valueOf(0) : e.getRequireCheck());
                    nodeResVO.setRecordId(e.getRecordId());
                    nodeResVO.setRecordName(e.getItemName());
                    nodeResVO.setType(2);
                    return nodeResVO;
                }).collect(Collectors.toList());
        basisDiagnosisNodeResVOS.addAll(collect);

        basisPrimaryResultResVO.setNodeList(basisDiagnosisNodeResVOS);
        return basisPrimaryResultResVO;
    }


    /**
     * 预期诊断结果
     */
    private ExpertDiagnosisResultResVO creatExpertDiagnosisResult(MedicalRec medicalRec, List<Disease> diseasesList, String processId) {
        ExpertDiagnosisResultResVO expertDiagnosisResultResVO = new ExpertDiagnosisResultResVO();
        expertDiagnosisResultResVO.setDiagnosis(diseasesList.stream().map(Disease::getDiseaseNameAlias).collect(Collectors.joining(";")));
        Set<String> expertDiseaseIdSet = diseasesList.stream().map(Disease::getId).collect(Collectors.toSet());
        // 从vp_diagnosis_primary中找到确认诊断时,确认的
        List<DiagnosisPrimary> userPrimaryDiseaseList = diagnosisPrimaryService.lambdaQuery()
                .eq(DiagnosisPrimary::getProcessId, processId)
                // 1确诊
                .eq(DiagnosisPrimary::getExcludeFlag, 1).list();

        List<Disease> userDiagnosisDiseaseList = CollUtil.isEmpty(userPrimaryDiseaseList) ? CollUtil.newArrayList()
                : diseaseService.listByIds(userPrimaryDiseaseList.stream().map(DiagnosisPrimary::getPrimaryDiagnosisId).collect(Collectors.toList()));
        // 用户的诊断
        expertDiagnosisResultResVO.setUserDiagnosisResult(userDiagnosisDiseaseList.stream().map(e -> {
            DiagnosisDiseaseResultNodeVO userNode = new DiagnosisDiseaseResultNodeVO();
            userNode.setDiseaseName(e.getDiseaseNameAlias());
            // 如果用户诊断在预期诊断里面,则正确,否则错误
            userNode.setCorrect(expertDiseaseIdSet.contains(e.getId()) ? 1 : 0);
            userNode.setDiseaseId(e.getId());
            return userNode;
        }).collect(Collectors.toList()));

        // 预期诊断
        expertDiagnosisResultResVO.setExpertDiagnosisResult(diseasesList.stream().map(e -> {
            DiagnosisDiseaseResultNodeVO node = new DiagnosisDiseaseResultNodeVO();
            node.setDiseaseName(e.getDiseaseNameAlias());
            node.setDiseaseId(e.getId());
            return node;
        }).collect(Collectors.toList()));
        return expertDiagnosisResultResVO;
    }


    public static <K> Predicate<K> distinctPredicateNotNull(Function<K, Object> function) {
        ConcurrentHashMap<Object, Boolean> map = new ConcurrentHashMap<>();

        return t -> null != function.apply(t) && null == map.putIfAbsent(function.apply(t), true);

    }

}