package thyyxxk.webserver.service.zhuyuanyisheng.emr;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.constants.Message;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.emr.EmrControlDaoV2;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.socketmessage.ApiMessageBody;
import thyyxxk.webserver.entity.zhuyuanyisheng.emr.EmrAuditDetail;
import thyyxxk.webserver.entity.zhuyuanyisheng.emr.EmrProgressNote;
import thyyxxk.webserver.entity.zhuyuanyisheng.emr.EmrRuleData;
import thyyxxk.webserver.entity.zhuyuanyisheng.emr.ProblemQualityControl;
import thyyxxk.webserver.entity.zhuyuanyisheng.emrcontrolrule.EmrRuleV2;
import thyyxxk.webserver.entity.zhuyuanyisheng.emrcontrolrule.FinalQuery;
import thyyxxk.webserver.entity.zhuyuanyisheng.emrcontrolrule.QualityControlAnalysisOfMedicalRecords;
import thyyxxk.webserver.service.externalhttp.WebSocketService;
import thyyxxk.webserver.service.externalhttp.emr.EmrEditor;
import thyyxxk.webserver.utils.*;

import javax.websocket.Session;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Slf4j
@DS("his")
public class EmrControlRuleSeverV2 {
    private final EmrControlDaoV2 dao;
    private final EmrEditor editor;
    private final WebSocketService socketService;
    private static final ConcurrentHashMap<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    public EmrControlRuleSeverV2(EmrControlDaoV2 dao, EmrEditor editor, WebSocketService socketService) {
        this.dao = dao;
        this.editor = editor;
        this.socketService = socketService;
    }

    public List<QualityControlAnalysisOfMedicalRecords> getPatients(QueryWrapper<?> qw) {
        return dao.queryEmrRulePatient(qw, "zy_actpatient");
    }

    /**
     * 环节质控
     *
     * @param start
     * @param end
     * @return
     */
    public ResultVo<JSONObject> specifyAdmissionQualityControl(String start, String end) {
        List<QualityControlAnalysisOfMedicalRecords> patInfo = dao.selectSuggestionByTimeAndFinalControl(start, end, 0, " ");
        if (ListUtil.isBlank(patInfo)) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "没有查询到患者信息。");
        }
        return ResultVoUtil.success(qualityControlOfMedicalRecords(patInfo, null, 0));
    }

    public ResultVo<List<EmrAuditDetail>> myPatientQualityControl() {
        List<EmrAuditDetail> list = dao.getUnprocessedRecommendationsByDoctor(TokenUtil.getInstance().getTokenUserId());

        return ResultVoUtil.success(list);
    }


    public JSONObject qualityControlOfMedicalRecords(List<QualityControlAnalysisOfMedicalRecords> patInfo,
                                                     String socketId,
                                                     Integer finalControl) {
        List<EmrRuleData> ruleData = dao.selectEmrRule();
        ProblemQualityControl error = new ProblemQualityControl();

        JSONObject js = new JSONObject();
        js.put("count", patInfo.size());
        String userCode = TokenUtil.getInstance().getTokenUserId();

        boolean pullThePlug = false;

        for (int i = 0; i < patInfo.size(); i++) {

            if (pullThePlug) {
                log.info("连接断开，循环关闭。");
                return null;
            }

            QualityControlAnalysisOfMedicalRecords p = patInfo.get(i);
            // 设置患者附加的信息
            setAdditionalInfo(p);
            for (EmrRuleData rule : ruleData) {
                try {
                    EmrRuleV2 ruleV2 = rule.getRule();
                    error.setName(ruleV2.getName());

                    if (rule.getType() == 1) {
                        int count = qualityControlTimeLimit(p, error, ruleV2);
                        p.setAccordingToTheNumberOfDefects(count);
                    }
                    // 这个先不要
//                    if (rule.getType() == 2) {
//                        executedAfterTheEvent(p, error, ruleV2);
//                    }
                    if (rule.getType() == 3) {
                        int count = thingsToDoBeforeTheEvent(p, error, ruleV2);
                        p.setAccordingToTheNumberOfDefects(count);
                    }
                } catch (Exception ignore) {
                }

            }

            error.setName("病程间隔");
            int count = intervalBetweenEpisodesOfTheCourseOfIllness(p, error);
            p.setAccordingToTheNumberOfDefects(count);

            int requiteCount = 病程记录结果回报(p, error);
            p.setAccordingToTheNumberOfDefects(requiteCount);

            // 查看患者的质控意见
            List<EmrAuditDetail> details = dao.patientQualityControl(p.getInpatientNo(), p.getAdmissTimes(), finalControl);
            if (ListUtil.notBlank(details)) {
                for (EmrAuditDetail detail : details) {
                    p.setQualityControlOpinions(detail);
                    p.setScore(detail.getScoringCriteria(), detail.getNumberOfDefects());
                }
            }
            js.put("currentIndex", i + 1);
            try {
                if (StringUtil.notBlank(socketId)) {
                    Session session = SESSION_MAP.get(socketId);
                    if (session == null) {
                        pullThePlug = true;
                    } else {
                        session.getBasicRemote().sendText(SocketMsg.socketVo(Message.EMR_RULE, js));
                    }
                } else {
                    String message = SocketMsg.socketVo(Message.EMR_RULE, js);
                    ApiMessageBody messageBody = new ApiMessageBody(userCode, message);
                    socketService.sendMessageByUserCode(messageBody);
                }
            } catch (Exception ignore) {
            }

        }
        log.info("ruleData:{}", JSON.toJSONString(error.returnsData()));
        return error.returnsData();
    }


    /**
     * 患者时限质控
     *
     * @param p     患者
     * @param error 错误信息
     */
    private int qualityControlTimeLimit(QualityControlAnalysisOfMedicalRecords p, ProblemQualityControl error, EmrRuleV2 ruleV2) {
        List<Date> timeSource = dao.executeSqlDateList(ruleV2.getTimeSource(p.getInpatientNo(), p.getAdmissTimes()));
        List<Date> executionSource = dao.executeSqlDateList(ruleV2.getExecutionSource(p.getInpatientNo(), p.getAdmissTimes()));

        int errorCount = 0;

        if (ListUtil.isBlank(timeSource)) {
            return errorCount;
        }

        for (int i = 0; i < timeSource.size(); i++) {
            Date a = timeSource.get(i);
            Date b;
            Map<String, String> errorMap = new HashMap<>();
            errorMap.put("start", DateUtil.formatDatetime(a));
            Date c = DateUtil.addDateHour(a, ruleV2.getPrescription());
            errorMap.put("gfsj", DateUtil.formatDatetime(c));
            try {
                b = executionSource.get(i);
                if (b == null) {
                    throw new NullPointerException();
                }
                errorMap.put("end", DateUtil.formatDatetime(b));
            } catch (Exception e) {
                Date newDate = new Date();
                if (DateUtil.shiJianDaXiao(newDate, c, ">")) {
                    long hour = DateUtil.theDifferenceIsHours(newDate, c);
                    errorMap.put("cs", String.valueOf(hour));

                    if (timeSource.size() == 1) {
                        error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap));
                    } else {
                        error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap) + " ", i);
                    }
                    errorCount++;
                }
                continue;
            }

            long hour = DateUtil.theDifferenceIsHours(b, a);
            errorMap.put("cs", String.valueOf(hour));

            boolean whetherToTimeOut = hour > ruleV2.getPrescription();
            if (whetherToTimeOut) {
                if (timeSource.size() == 1) {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutComplete(), errorMap));
                } else {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutComplete(), errorMap) + " ", i);
                }
                errorCount++;
            }
        }
        return errorCount;
    }

    private int intervalBetweenEpisodesOfTheCourseOfIllness(QualityControlAnalysisOfMedicalRecords p, ProblemQualityControl error) {
        int count = 0;
        List<EmrProgressNote> dateList = dao.fragmentInterval(p.getInpatientNo(), p.getAdmissTimes());
        if (ListUtil.isBlank(dateList)) {
            return 0;
        }
        int aging;
        Date seriouslyIll = dao.whetherThereIsASeriousIllnessDoctorSOrder(p.getInpatientNo(), p.getAdmissTimes(), "病重");
        Date beCriticallyIll = dao.whetherThereIsASeriousIllnessDoctorSOrder(p.getInpatientNo(), p.getAdmissTimes(), "病危");
        String name;
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < dateList.size(); i++) {
            Date start = dateList.get(i).getCreationTime();
            if (seriouslyIll != null && DateUtil.shiJianDaXiao(start, seriouslyIll, ">")) {
                aging = 48;
                name = "病重";
            } else if (beCriticallyIll != null && DateUtil.shiJianDaXiao(start, beCriticallyIll, ">")) {
                aging = 24;
                name = "病危";
            } else {
                aging = 72;
                name = "普通";
            }
            Date end;
            try {
                end = dateList.get(i + 1).getCreationTime();
            } catch (Exception e) {
                continue;
            }
            long hour = DateUtil.theDifferenceIsHours(end, start);
            boolean whetherToTimeOut = hour > aging;
            if (whetherToTimeOut) {
                count++;
                sb.append("患者状态：【").append(name).append("】")
                        .append(dateList.get(i).getName())
                        .append(":【").append(DateUtil.formatDatetime(start)).append("】和")
                        .append(dateList.get(i + 1).getName())
                        .append("：【").append(DateUtil.formatDatetime(end)).append("】")
                        .append("间隔超").append(hour).append("小时。");

            }
        }
        if (StringUtil.notBlank(sb.toString())) {
            error.setTheErrorMessage(p, sb.toString());
        }
        return count;
    }

    private void executedAfterTheEvent(QualityControlAnalysisOfMedicalRecords p, ProblemQualityControl error, EmrRuleV2 ruleV2) {
        int count = 0;
        Map<String, String> errMap = new HashMap<>();
        errMap.put("pat_no", p.getInpatientNo());
        errMap.put("times", String.valueOf(p.getAdmissTimes()));
        Date start = dao.executeSqlDate(ruleV2.getTimeSource(p.getInpatientNo(), p.getAdmissTimes()));
        if (start == null) {
            return;
        }
        errMap.put("start", DateUtil.formatDatetime(start));
        String tempEndSql = StringUtil.replaceElementValue(ruleV2.getExecutionSource(), errMap);
        Date end = dao.executeSqlDate(tempEndSql);

        if (end == null) {
            // 规范时间
            Date normalizeTime = DateUtil.addDateHour(start, ruleV2.getPrescription());
            errMap.put("gfsj", DateUtil.formatDatetime(normalizeTime));
            Date newDate = new Date();
            // 如果当前的时间大于了规范的时间，那么就要判断超时了多久
            if (DateUtil.shiJianDaXiao(newDate, normalizeTime, ">")) {
                long hour = DateUtil.theDifferenceIsHours(newDate, normalizeTime);
                errMap.put("cs", String.valueOf(hour));
                error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errMap));
            }
        } else {
            long hour = DateUtil.theDifferenceIsHours(end, start);
            boolean whetherToTimeOut = hour > ruleV2.getPrescription();
            if (whetherToTimeOut) {
                errMap.put("cs", String.valueOf(hour));
                error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutComplete(), errMap));
            }
        }

    }

    private int thingsToDoBeforeTheEvent(QualityControlAnalysisOfMedicalRecords p, ProblemQualityControl error, EmrRuleV2 ruleV2) {
        int errorCount = 0;
        List<Date> startList = dao.executeSqlDateList(ruleV2.getTimeSource(p.getInpatientNo(), p.getAdmissTimes()));
        if (ListUtil.isBlank(startList)) {
            return 0;
        }

        List<Date> endList = dao.executeSqlDateList(ruleV2.getExecutionSource(p.getInpatientNo(), p.getAdmissTimes()));
        if (ListUtil.isBlank(endList)) {
            error.setTheErrorMessage(p, "未创建");
            return startList.size();
        }

        for (int i = 0, len = startList.size(); i < len; i++) {
            Map<String, String> errorMap = new HashMap<>();

            Date start = startList.get(i);
            errorMap.put("start", DateUtil.formatDatetime(start));
            Date end;
            try {
                end = endList.get(i);
                if (end == null) {
                    throw new NullPointerException();
                }
                errorMap.put("end", DateUtil.formatDatetime(end));
            } catch (Exception e) {
                if (startList.size() == 1) {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap));
                } else {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap) + " ", i);
                }
                errorCount++;
                continue;
            }

            if (DateUtil.shiJianDaXiao(start, end, "<")) {
                if (startList.size() == 1) {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap));
                } else {
                    error.setTheErrorMessage(p, StringUtil.replaceElementValue(ruleV2.getTimeoutNotWritten(), errorMap) + " ", i);
                }
                errorCount++;
            }

        }
        return errorCount;
    }

    private static int theNumberOfObjects(JSONObject js, String name) {
        try {
            Object a = js.getJSONObject(name);
            if (a == null) {
                return 0;
            }
            return 1;
        } catch (Exception ignored) {

        }

        try {
            JSONArray list = js.getJSONArray(name);
            if (ListUtil.isBlank(list)) {
                return 0;
            }
            return list.size();
        } catch (Exception ignored) {
        }
        return 0;
    }

    public int 病程记录结果回报(QualityControlAnalysisOfMedicalRecords p, ProblemQualityControl error) {
        int errorCount = 0;
        String id = dao.selectedCourseId(p.getInpatientNo(), p.getAdmissTimes());
        if (id == null) {
            return errorCount;
        }
        String codes = "CT检查结果,磁共振检查结果,病理结果,细菌培养结果";
        try {
            JSONObject js = editor.getEmrBusinessByDocumentId(id, codes);
            int count;
            log.info("患者数据:{}", js);
            // CT检查结果 回报
            int checkTheResultsCount = dao.checkTheResultsCount(p.getInpatientNo(), p.getAdmissTimes());
            error.setName("CT检查结果回报");
            if (checkTheResultsCount > 0) {
                count = theNumberOfObjects(js, "CT检查结果");
                if (count != checkTheResultsCount) {
                    errorCount++;
                    error.setTheErrorMessage(p, String.format("CT检查【%d】条，病程：【%d】条", checkTheResultsCount, count));
                }
            }

            // 磁共振检查结果
            int magneticResonanceCount = dao.magneticResonanceCount(p.getInpatientNo(), p.getAdmissTimes());
            error.setName("磁共振检查回报");
            if (magneticResonanceCount > 0) {
                count = theNumberOfObjects(js, "磁共振检查结果");
                if (count != magneticResonanceCount) {
                    errorCount++;
                    error.setTheErrorMessage(p, String.format("磁共振检查结果【%d】条，病程：【%d】条", magneticResonanceCount, count));
                }
            }

            // 病理结果
            int numberOfPathologies = dao.numberOfPathologies(p.getInpatientNo(), p.getAdmissTimes());
            error.setName("病理结果回报");
            if (numberOfPathologies > 0) {
                count = theNumberOfObjects(js, "病理结果");
                if (count != numberOfPathologies) {
                    errorCount++;
                    error.setTheErrorMessage(p, String.format("病理结果【%d】条，病程：【%d】条", numberOfPathologies, count));
                }
            }

            // 细菌培养结果
            int bacterialCultureCount = dao.bacterialCultureCount(p.getInpatientNo(), p.getAdmissTimes());
            error.setName("细菌培养回报");
            if (bacterialCultureCount > 0) {
                count = theNumberOfObjects(js, "细菌培养结果");
                if (count != bacterialCultureCount) {
                    errorCount++;
                    error.setTheErrorMessage(p, String.format("细菌培养【%d】条，病程：【%d】条", bacterialCultureCount, count));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return errorCount;
    }


    public ResultVo<List<QualityControlAnalysisOfMedicalRecords>> terminalPatients(FinalQuery param) {

        StringBuilder sql = new StringBuilder();

        sql.append(" where 1=1 ");
        if (ListUtil.notBlank(param.getDeptCode())) {
            String deptList = "(" + param.getDeptCode().stream().map(s -> "'" + s + "'").collect(Collectors.joining(",")) + ")";

            sql.append(" and small_dept in ").append(deptList);
        }

        if (StringUtil.notBlank(param.getTubeBedDoctor())) {
            sql.append(" and refer_physician = ").append("'").append(param.getTubeBedDoctor()).append("'");
        }

        List<QualityControlAnalysisOfMedicalRecords> list = dao.selectSuggestionByTimeAndFinalControl(param.getStartDate(), param.getEndDate(), 1, sql.toString());

        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.fail(ExceptionEnum.NO_DATA_EXIST);
        }
        JSONObject temp = qualityControlOfMedicalRecords(list, param.getSocketId(), 1);
        if (temp == null) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "连接断开循环终止。");
        }
        return ResultVoUtil.success(list);
    }

    /**
     * 设置患者附加的信息
     *
     * @param p 患者
     */
    private void setAdditionalInfo(QualityControlAnalysisOfMedicalRecords p) {
        List<JSONObject> list = dao.transfer(p.getInpatientNo(), p.getAdmissTimes());
        if (ListUtil.notBlank(list)) {
            StringBuilder zkName = new StringBuilder();
            for (int i = 0; i < list.size(); i++) {
                JSONObject item = list.get(i);
                if (i == 0) {
                    zkName.append(item.getString("fDept")).append("->").append(item.getString("dept"));
                } else {
                    zkName.append("->").append(item.getString("dept"));
                }
            }
            p.setZkListName(zkName.toString());
        }
        JSONObject objects = dao.objects(p.getInpatientNo(), p.getAdmissTimes());
        try {
            if (objects != null) {
                JSONObject dataElement = objects.getJSONObject("data_element");
                JSONArray array = dataElement.getJSONArray("病例分型");
                if (array != null) {
                    JSONObject typing = array.getJSONObject(0);
                    p.setTyping(typing.getString("name"));
                }
                JSONArray diagnosisOfAdmission = dataElement.getJSONArray("入院诊断");
                if (diagnosisOfAdmission != null) {
                    p.setAdmissDiag(diagnosisOfAdmission.getJSONObject(0).getString("name"));
                }
            }
        } catch (Exception ignore) {

        }

    }

    public static void openSocket(Session session, String sid) {
        SESSION_MAP.put(sid, session);
    }

    public static void closeSocket(String sid) {
        SESSION_MAP.remove(sid);
    }



}
