package thyyxxk.webserver.service.zhuyuanyiji;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.emr.EmrPatientDao;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.patientClinicalPathway.ClinicalPathwayPatientDao;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.patientClinicalPathway.ClinicalPathwayPatientDayLogDao;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.patientClinicalPathway.ClinicalPathwayPatientWorkLogDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.dictionary.pathwayTemplates.dto.ClinicalPathwayTemplates;
import thyyxxk.webserver.entity.dictionary.pathwayTemplates.dto.ClinicalPathwayWork;
import thyyxxk.webserver.entity.dictionary.pathwayTemplates.vo.TemplateDayVo;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.dto.ClinicalPathwayPatient;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.dto.ClinicalPathwayPatientDayLog;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.dto.ClinicalPathwayPatientWorkLog;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.req.ExitClinicalPathwayReq;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.req.PatientEnrollmentReq;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.req.UpdatePatientDayReq;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.vo.ClinicalPathwayPatientWorkLogVo;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.vo.PatientDayWorkVo;
import thyyxxk.webserver.entity.zhuyuanyisheng.patientClinicalPathway.vo.PatientOrderDayVo;
import thyyxxk.webserver.enums.ClinicalPathwayPatientState;
import thyyxxk.webserver.service.dictionary.clinicalPathway.ClinicalPathwayService;
import thyyxxk.webserver.service.hutoolcache.ClinicalPathwayCache;
import thyyxxk.webserver.utils.DateUtil;
import thyyxxk.webserver.utils.ListUtil;
import thyyxxk.webserver.utils.ResultVoUtil;
import thyyxxk.webserver.utils.SnowFlakeId;

import java.util.*;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
@Slf4j
public class ClinicalPathwayPatientService {
    private final ClinicalPathwayPatientDao dao;
    private final EmrPatientDao emrPatientDao;
    private final ClinicalPathwayService clinicalPathwayService;
    private final ClinicalPathwayCache cache;
    private final ClinicalPathwayPatientDayLogDao dayDao;

    /**
     * 获取患者的入院诊断来推荐可以进入的临床路径
     * 根据患者的入院诊断，在电子中提取
     *
     * @param patNo
     * @param times
     * @return
     */
    public ResultVo<List<ClinicalPathwayTemplates>> recommendedClinicalPathway(String patNo, Integer times) {
        String diagnosisStr = emrPatientDao.obtainAdmissionDiagnosis(patNo, times);
        if (StrUtil.isBlank(diagnosisStr)) {
            return ResultVoUtil.success();
        }
        JSONObject entries = JSONUtil.parseObj(diagnosisStr);
        JSONArray diagnosis = entries.getByPath("入院诊断.value", JSONArray.class);
        if (ListUtil.isBlank(diagnosis)) {
            return ResultVoUtil.success();
        }
        JSONObject diagnosisObj = diagnosis.getJSONObject(0);
        if (diagnosisObj == null) {
            return ResultVoUtil.success();
        }
        return ResultVoUtil.success(dao.getTemplateByDiagnose(diagnosisObj.getStr("code")));
    }

    /**
     * 患者进入到路径中
     *
     * @param req
     * @return
     */
    public ResultVo<String> patientEnrollment(PatientEnrollmentReq req) {
        ClinicalPathwayPatient patient = dao.selectPatientClinicalPathway(req.getPatNo(), req.getTimes());
        if (patient != null) {
            if (ClinicalPathwayPatientState.入径.getCode().equals(patient.getState())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者还在路径中请先选择，退出路径才能选择。");
            }
            if (patient.getTemplateId().equals(req.getTemplateId())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者入径不能和上次一样，请勿重复选择。");
            }
        }
        ClinicalPathwayPatient build = ClinicalPathwayPatient.builder()
                .id(SnowFlakeId.instance().nextId())
                .patNo(req.getPatNo())
                .times(req.getTimes())
                .templateId(req.getTemplateId())
                .state(ClinicalPathwayPatientState.入径.getCode())
                .build();
        dao.insert(build);
        return ResultVoUtil.success();
    }

    /**
     * 获取患者进入到的临床路径
     *
     * @param patNo
     * @param times
     * @return
     */
    public ResultVo<List<TemplateDayVo>> getClinicalPathway(String patNo, Integer times) {
        ClinicalPathwayPatient patient = dao.selectPatientClinicalPathway(patNo, times);
        if (patient == null) {
            return ResultVoUtil.success(new ArrayList<>());
        }
        List<TemplateDayVo> templateDay = clinicalPathwayService.getTemplateDayNotContent(patient.getTemplateId());
        return ResultVoUtil.success(templateDay);
    }


    /**
     * 获取患者每天的执行日志
     *
     * @param patNo 患者编号
     * @param times 时间
     * @return 患者每天的执行日志
     */
    public List<PatientDayWorkVo> getClinicalPathwayDayLog(String patNo, Integer times) {
        // 根据患者编号和时间获取患者每天的执行日志
        List<PatientDayWorkVo> dayLogByPatientNo = dao.getDayLogByPatientNo(patNo, times);

        // 如果获取到的日志为空，则返回一个空列表
        if (ListUtil.isBlank(dayLogByPatientNo)) {
            return new ArrayList<>();
        }

        // 根据患者编号和时间获取患者的工作日志
        List<ClinicalPathwayPatientWorkLogVo> patientWorkLogByPatientNo = dao.getPatientWorkLogByPatientNo(patNo, times);

        // 如果获取到的工作日志为空，则将每天的执行日志设置为空
        if (ListUtil.isBlank(patientWorkLogByPatientNo)) {
            for (PatientDayWorkVo item : dayLogByPatientNo) {
                item.setExecute(getEmptyMap());
            }
        } else {
            // 将工作日志按照天分组
            Map<String, List<ClinicalPathwayPatientWorkLogVo>> workMap = patientWorkLogByPatientNo.stream().collect(
                    Collectors.groupingBy(
                            ClinicalPathwayPatientWorkLogVo::getDayId
                    )
            );

            // 遍历每天的执行日志
            dayLogByPatientNo.forEach(day -> {
                // 创建一个空的临时Map
                Map<String, List<ClinicalPathwayPatientWorkLogVo>> tmpMap = getEmptyMap();
                // 获取当天的工作日志
                List<ClinicalPathwayPatientWorkLogVo> clinicalPathwayWorks = workMap.get(day.getId());
                // 如果当天有工作日志
                if (clinicalPathwayWorks != null) {
                    // 遍历工作日志
                    for (ClinicalPathwayPatientWorkLogVo item : clinicalPathwayWorks) {
                        // 获取工作日志的类型
                        List<ClinicalPathwayPatientWorkLogVo> compute = tmpMap.get(ClinicalPathwayService.workTYpe[item.getType()]);
                        // 将工作日志添加到临时Map中
                        compute.add(item);
                    }
                }
                // 将临时Map设置到每天的执行日志中
                day.setExecute(tmpMap);
            });
        }

        // 返回每天的执行日志
        return dayLogByPatientNo;
    }

    private Map<String, List<ClinicalPathwayPatientWorkLogVo>> getEmptyMap() {
        Map<String, List<ClinicalPathwayPatientWorkLogVo>> tmpMap = new HashMap<>();
        tmpMap.put("zhenliao", new ArrayList<>());
        tmpMap.put("yzchangqi", new ArrayList<>());
        tmpMap.put("yzlinshi", new ArrayList<>());
        tmpMap.put("huli", new ArrayList<>());
        return tmpMap;
    }

    @Data
    public static class GenerateParams {
        private List<TemplateDayVo> dayValue;
        private List<ClinicalPathwayPatientDayLog> execDays;
        private long betweenDay;
        private String patNo;
        private int times;
        private Date createTime;
    }

    /**
     * 判断是否需要生成今天的临床路径
     * generatePath
     *
     * @param patNo 住院号
     * @param times 住院次数
     * @return ok
     */
    public ResultVo<String> generatePath(String patNo, Integer times) {
        // 根据住院号和住院次数查询患者临床路径信息
        ClinicalPathwayPatient patient = dao.selectPatientClinicalPathway(patNo, times);
        // 如果患者临床路径信息为空，则返回失败信息
        if (patient == null) {
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST, "患者未入经无法进入下一个日期。");
        }

        // 如果患者临床路径状态为完成，则返回成功信息
        if (ClinicalPathwayPatientState.完成.getCode().equals(patient.getState())) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_NOTIFICATION, "患者完成了全部的路径");
        }

        // 如果不是入径的话就不执行
        if (!ClinicalPathwayPatientState.入径.getCode().equals(patient.getState())) {
            return ResultVoUtil.success();
        }

        Date createTime = patient.getCreateTime();
        Date now = new Date();
        // 判断当前时间和入径的时间相差几天
        long betweenDay = DateUtil.betweenDay(now, createTime, true);
        List<ClinicalPathwayPatientDayLog> execDays = dao.selectExecDay(patNo, times, patient.getTemplateId(), DateUtil.formatDate(patient.getCreateTime(), DateUtil.DATE));
        int execDay = execDays.size();

        // 如果一次都没执行的话就判断一下有无上一次入径
        if (execDay == 0) {
            checkPatientPathway(patNo, times);
        }
        // 判断已经执行了几天和这个模板需要执行几天，如果时间一样的话就说明完成了临床路径，如果是第三天的话就需要多点一次了
        if (execDay == dao.execDayByTemplateId(patient.getTemplateId())) {
            ClinicalPathwayPatient updateParams = new ClinicalPathwayPatient();
            updateParams.setId(patient.getId());
            updateParams.setState(ClinicalPathwayPatientState.完成.getCode());
            dao.updateById(updateParams);
            // 完成了临床路径
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_NOTIFICATION, "患者完成了全部的路径");
        }

        List<TemplateDayVo> templateDay = cache.getTemplateDayNotContent(patient.getTemplateId(), () -> clinicalPathwayService.getTemplateDay(patient.getTemplateId()));

        GenerateParams params = new GenerateParams();
        params.setBetweenDay(betweenDay + 1);
        params.setDayValue(templateDay);
        params.setExecDays(execDays);
        params.setPatNo(patNo);
        params.setTimes(times);
        params.setCreateTime(patient.getCreateTime());
        whatDayDoesItNeedToGenerate(params);
        return ResultVoUtil.success();
    }

    public void checkPatientPathway(String patNo, Integer times) {
        // 根据patNo和times查询患者信息
        List<ClinicalPathwayPatient> patients = dao.selectPatientCount(patNo, times);
        // 如果患者信息只有一个，则直接返回
        if (patients.size() == 1) {
            return;
        }
        // 获取患者信息
        ClinicalPathwayPatient patient = patients.get(0);
        // 获取患者创建时间
        Date createTime = patient.getCreateTime();
        // 获取当前时间
        Date now = new Date();
        // 执行几天了
        long betweenDay = DateUtil.betweenDay(createTime, now, true);
        log.info("判断是否需要删除一天：{}", betweenDay);
    }

    public void whatDayDoesItNeedToGenerate(GenerateParams params) {
        List<TemplateDayVo> tmpDay = new ArrayList<>();
        for (TemplateDayVo item : params.getDayValue()) {
            for (int i = 0; i < item.getExecutionDay(); i++) {
                tmpDay.add(item);
            }
        }
        int size = params.getExecDays().size();
        // 应该需要生成几天的
        for (int i = 0; i < params.getBetweenDay(); i++) {
            Date createTime = DateUtil.addNaturalDays(params.getCreateTime(), i + 1);
            // 当前的天数，小于患者指定生成的天数的话就需要生成
            if (i > size - 1) {
                TemplateDayVo templateDayVo = tmpDay.get(i);
                String id = generateDays(templateDayVo, params, createTime);
                generateWorks(templateDayVo, params, id);
            }
        }
    }

    /**
     * 生成这一天的
     *
     * @param value
     * @param params
     */
    public String generateDays(TemplateDayVo value, GenerateParams params, Date createTime) {
        ClinicalPathwayPatientDayLog day = new ClinicalPathwayPatientDayLog();
        day.setId(SnowFlakeId.instance().nextId());
        day.setDayId(value.getId());
        day.setPatNo(params.getPatNo());
        day.setTimes(params.getTimes());
        day.setState(ClinicalPathwayPatientState.入径.getCode());
        day.setTemplateId(value.getTemplateId());
        day.setExecDate(DateUtil.beginOfDay(createTime));
        dayDao.insert(day);
        return day.getId();
    }

    /**
     * 生成工作内容
     *
     * @param value
     * @param params
     */
    public void generateWorks(TemplateDayVo value, GenerateParams params, String id) {
        // 创建一个ClinicalPathwayPatientWorkLog的列表
        List<ClinicalPathwayPatientWorkLog> insertData = new ArrayList<>();
        // 遍历value的execute属性
        for (Map.Entry<String, List<ClinicalPathwayWork>> item : value.getExecute().entrySet()) {
            // 获取execute属性的值
            List<ClinicalPathwayWork> value1 = item.getValue();
            // 遍历execute属性的值
            for (ClinicalPathwayWork clinicalPathwayWork : value1) {
                // 创建一个ClinicalPathwayPatientWorkLog对象
                ClinicalPathwayPatientWorkLog work = new ClinicalPathwayPatientWorkLog();
                // 设置id
                work.setId(SnowFlakeId.instance().nextId());
                // 设置dayId
                work.setDayId(id);
                // 判断key是否为huli或zhenliao
                if (item.getKey().equals("huli") || item.getKey().equals("zhenliao")) {
                    // 设置complete为1
                    work.setComplete(1);
                } else {
                    // 设置complete为0
                    work.setComplete(0);
                }
                // 设置patNo
                work.setPatNo(params.getPatNo());
                // 设置times
                work.setTimes(params.getTimes());
                // 设置contentId
                work.setContentId(clinicalPathwayWork.getId());
                // 将work对象添加到insertData列表中
                insertData.add(work);
            }
        }
        // 批量插入insertData列表中的数据
        ListUtil.batchList(insertData, ClinicalPathwayPatientWorkLogDao.class, BaseMapper::insert);
    }

    public ResultVo<String> updatePatientDay(UpdatePatientDayReq params) {
        ClinicalPathwayPatientDayLog dayLog = new ClinicalPathwayPatientDayLog();
        dayLog.setId(params.getId());
        dayLog.setDescribe(params.getDescribe());
        dayDao.updateById(dayLog);

        ListUtil.batchList(params.getWorks(), ClinicalPathwayPatientWorkLogDao.class, (m, i) -> {
            ClinicalPathwayPatientWorkLog log = new ClinicalPathwayPatientWorkLog();
            log.setId(i.getWorkId());
            log.setComplete(i.getComplete());
            m.updateById(log);
        });
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_NOTIFICATION);
    }


    public Map<String, List<PatientOrderDayVo>> getPatientOrderByDay(String patNo, Integer times, String dayId) {
        List<PatientOrderDayVo> patientOrderWork = dao.getPatientOrderWork(patNo, times, dayId);

        return patientOrderWork.stream().collect(
                Collectors.groupingBy(PatientOrderDayVo::getWorkContent)
        );
    }

    public ResultVo<String> exitClinicalPathway(ExitClinicalPathwayReq req) {
        ClinicalPathwayPatient patient = dao.selectPatientClinicalPathway(req.getPatNo(), req.getTimes());
        if (!ClinicalPathwayPatientState.入径.getCode().equals(patient.getState())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者已经出经了。");
        }
        ClinicalPathwayPatient update = new ClinicalPathwayPatient();
        update.setId(patient.getId());
        update.setState(req.getState());
        update.setReason(req.getReason());
        // todo 判断是否需要删除当天的数据
        dao.updateById(update);
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_NOTIFICATION, "操作成功您现在可以重新入径了。");
    }


}
