package thyyxxk.webserver.service.zhuyuanyisheng.yizhuverify;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import thyyxxk.webserver.config.envionment.YzConfig;
import thyyxxk.webserver.constants.Capacity;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.YiZhuLuRuDao;
import thyyxxk.webserver.entity.exceedingDischargeDays.ExceedingDischargeDays;
import thyyxxk.webserver.entity.login.UserInfo;
import thyyxxk.webserver.entity.medicine.YpZdGroupName;
import thyyxxk.webserver.entity.yzcheck.ChargeLimitations;
import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.XinZhenYiZhu;
import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.XinZhenYzActOrder;
import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.YzZdOrderItemConfirm;
import thyyxxk.webserver.service.hutoolcache.ExtraCache;
import thyyxxk.webserver.utils.*;

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

@Slf4j
public class YiZhuCheckData {
    private final YiZhuLuRuDao dao;
    private final ExtraCache extraCache = SpringUtil.getBean(ExtraCache.class);
    private final YzConfig yzConfig = SpringUtil.getBean(YzConfig.class);

    /**
     * 医嘱数组
     */
    private List<XinZhenYzActOrder> list;

    /**
     * 药品
     */
    private final CacheOnce<XinZhenYzActOrder> drug = new CacheOnce<>();

    /**
     * 医生等级
     */
    private final CacheOnce<Integer> authorizedDoctorLevel = new CacheOnce<>();

    /**
     * 项目
     */
    private final CacheOnce<List<XinZhenYzActOrder>> project = new CacheOnce<>();

    /**
     * 医嘱
     */
    private final CacheOnce<XinZhenYzActOrder> yzItem = new CacheOnce<>();

    /**
     * 医保限制
     */
    private final CacheOnce<ChargeLimitations> yiBaoLimitation = new CacheOnce<>();

    /**
     * 患者信息
     */
    private XinZhenYiZhu patientInformation;
    private Boolean strictVerification = false;
    /**
     * 错误信息
     */
    List<String> errorMessage = new ArrayList<>();
    /**
     * 警告信息
     */
    List<String> warningMessage = new ArrayList<>();
    Map<String, Object> returnMap = new HashMap<>();
    private UserInfo userInfo;
    @Getter
    private Boolean passTheAudit = true;
    private final List<String> groupList = extraCache.getYf().get("xy").stream().map(YpZdGroupName::getGroupNo).collect(Collectors.toList());
    private boolean checkForRestrictedMedication = false;

    /**
     * 全排斥医嘱
     */
    @Getter
    private Repel repel = null;
    private final String ONCE = "ONCE";

    public YiZhuCheckData(YiZhuLuRuDao dao) {
        this.dao = dao;
    }

    public YiZhuCheckData init(List<XinZhenYzActOrder> list, XinZhenYiZhu patientInformation) {
        userInfo = dao.selectedUserInfoByCode(TokenUtil.getInstance().getTokenUserId());
        this.list = list;
        this.patientInformation = patientInformation;
        strictVerification = patientInformation != null;
        return this;
    }

    public YiZhuCheckData init(XinZhenYzActOrder orderData, XinZhenYiZhu patientInformation) {
        List<XinZhenYzActOrder> temp = Collections.singletonList(orderData);
        init(temp, patientInformation);
        return this;
    }

    public void judgeExclusion() {
        repel = new Repel();
    }

    public Map<String, Object> startCheck(Consumer<XinZhenYzActOrder> action) {
        for (XinZhenYzActOrder item : list) {
            clearErrorMessage();
            if (strictVerification) {
                strictVerificationFunc(item);
            }
            publicCheck(item);
            String ITEM = "00";
            if (item.getSerial().equals(ITEM)) {
                itemCheck(item);
            } else {
                drugCheck(item);
            }
            if (action != null) {
                action.accept(item);
            }
            setReturnMap(item.getActOrderNo().stripTrailingZeros().toPlainString());
        }

        return getReturnMap();
    }

    /**
     * 公共校验
     *
     * @param item 医嘱
     */
    private void publicCheck(XinZhenYzActOrder item) {
        if (StringUtil.isBlank(item.getOrderCode())) {
            errorMessage.add("项目编码不能为空");
        }
        if (StringUtil.isBlank(item.getOrderName())) {
            errorMessage.add("项目名称不能为空");
        }
        if (StringUtil.isBlank(item.getExecUnit())) {
            errorMessage.add("执行科室不能为空");
        } else if (item.getExecUnit().startsWith("8")) {
            errorMessage.add("执行科室不能选择为病区");
        }

        if (StringUtil.isBlank(item.getFrequCode())) {
            errorMessage.add("频次不能为空");
        } else if (dao.getTheFrequency(item.getFrequCode()) == 0) {
            errorMessage.add("该执行频率已被停用，请更改，不然会导致无法执行。");
        }
    }

    private void itemCheck(XinZhenYzActOrder item) {
        List<XinZhenYzActOrder> projectDetails = project.get(item.getOrderCode(), dao::itemDataOne);
        XinZhenYzActOrder yzItemInio = yzItem.get(item.getOrderCode(), dao::yzItem);

        if (yzItemInio != null) {
            if (yzItemInio.getDelFlag() == 1) {
                errorMessage.add("该医嘱已被停用，请联系物价。");
            }
        }


        List<String> ZK_CODE_LIST = new ArrayList<>();
        Map<String, YzZdOrderItemConfirm> specialMedicalAdvice = new HashMap<>();

        List<YzZdOrderItemConfirm> value = extraCache.getYzZdOrderItemConfirm();

        if (value != null) {
            for (YzZdOrderItemConfirm yzCode : value) {
                if (yzCode.getItemName().equals("转科")) {
                    ZK_CODE_LIST.add(yzCode.getOrderCode());
                }
                specialMedicalAdvice.put(yzCode.getOrderCode(), yzCode);
            }
        }


        if (strictVerification && specialMedicalAdvice.containsKey(item.getOrderCode())) {
            YzZdOrderItemConfirm confirm = specialMedicalAdvice.get(item.getOrderCode());
            if (confirm.getCount() > 0 && dao.selectCountByOrderCode(patientInformation.getInpatientNo(), patientInformation.getAdmissTimes(), item.getOrderCode()) > confirm.getCount()) {
                errorMessage.add("该医嘱只能开一条，请先作废上一条医嘱");
            }
        }

        if (ZK_CODE_LIST.contains(item.getOrderCode())) {
            if (StringUtil.isBlank(item.getZkWardCode())) {
                errorMessage.add("转科病房不能为空");
            }
            if (StringUtil.isBlank(item.getZkDeptCode())) {
                errorMessage.add("转科科室不能为空");
            }
        }


        // 如果这个项目下面没有费用明细，那么这就是一条口头医嘱，或者特殊医嘱
        if (ListUtil.notBlank(projectDetails)) {
            for (XinZhenYzActOrder detailed : projectDetails) {
                if (detailed.getDelFlag() == 1) {
                    errorMessage.add(String.format("项目：【%s】，已经被物价停用了", detailed.getOrderName()));
                }
                if (StringUtil.isBlank(detailed.getNationalCode())) {
                    warningMessage.add(String.format("项目：【%s】，没有匹配医保码", detailed.getOrderName()));
                }
            }
        }

        // 是否是排斥医嘱
        if (repel != null) {
            Integer paiChiYiZhu = dao.shiFouPaiChiYiZhu(item.getOrderCode());
            if (paiChiYiZhu != null && paiChiYiZhu.equals(1)) {
                repel.setCount(repel.getCount() + 1);
                repel.setOrderNo(item.getActOrderNo());
                repel.setDate(item.getStartTime());
            }

            if (yzConfig.getExceedingDischargeDays() != -1) {
                List<String> strings = extraCache.getYzZdOrderItemMap().get("出院");
                if (strings.contains(item.getOrderCode())) {
                    Date admissDate = cn.hutool.core.date.DateUtil.beginOfDay(patientInformation.getAdmissDate());
                    Date orderTime = cn.hutool.core.date.DateUtil.beginOfDay(item.getOrderTime());

                    long betweenDay = cn.hutool.core.date.DateUtil.betweenDay(orderTime, admissDate, false);
                    int exceedingDischargeDays = yzConfig.getExceedingDischargeDays();
                    // 根据病种诊断获取天数
                    List<ExceedingDischargeDays> exceedingDischargeDaysList = dao.selectExceedingDischargeDays(patientInformation.getAdmissDiag());
                    if(exceedingDischargeDaysList != null && exceedingDischargeDaysList.size() > 0){
                        exceedingDischargeDays = exceedingDischargeDaysList.get(0).getDischargeDays();
                    }

//                    if (betweenDay <= exceedingDischargeDays) {
//                        if (item.getSuperiorDoctor() == null || !item.getSuperiorDoctor().equals(patientInformation.getDeptDirector())) {
//                            errorMessage.add(String.format("患者的住院天数小于等于%d天请科主任授权，开出院医嘱。", exceedingDischargeDays));
//                        }
//                    }
                }
            }
        }
        item.setDrugOcc(item.getDrugQuan());
    }


    private void drugCheck(XinZhenYzActOrder item) {
        if (StringUtil.isBlank(item.getGroupNo())) {
            errorMessage.add("没有药房请重新开。");
            return;
        }

        if (!groupList.contains(item.getGroupNo())) {
            errorMessage.add("药房错误，请重新选择药房。");
        }

        String key = item.getOrderCode().trim() + item.getSerial().trim() + item.getGroupNo().trim();
        XinZhenYzActOrder detailsOfDrugs = getDrugData(key, item.getSupplyCode());

        if (BigUtils.bigXiaoYu(item.getDose(), 0)) {
            errorMessage.add(String.format("医嘱：【%s】，计量不能开负数", item.getOrderName()));
        }

        if (StringUtil.isBlank(item.getDrugSpecification())) {
            errorMessage.add("药品规格不能为空");
        }
        if (StringUtil.isBlank(item.getSupplyCode())) {
            errorMessage.add("给药方式不能为空");
        } else if (detailsOfDrugs.getSupplyCode() == null || "1".equals(detailsOfDrugs.getSupplyCode())) {
            errorMessage.add("给药方式已被停用。");
        }
        if (item.getDose() == null || BigUtils.dengYu(item.getDose(), 0)) {
            errorMessage.add("一次计量不能为空");
        }
        if (StringUtil.isBlank(item.getDoseUnit())) {
            errorMessage.add("计量单位不能为空");
        }
        if (StringUtil.isBlank(item.getSerial())) {
            errorMessage.add("包装大小不能为空");
        }

        if (detailsOfDrugs == null) {
            errorMessage.add("没有找到对应的药品，请联系药房。");
            return;
        }

        if (StringUtil.isBlank(item.getMiniUnit())) {
            if (item.getSerial().equals("01")) {
                item.setMiniUnit(detailsOfDrugs.getMiniUnit());
            } else {
                item.setMiniUnit(detailsOfDrugs.getPackUnit());
            }
        }
        if (detailsOfDrugs.getDelFlag() == 1) {
            errorMessage.add("药品已经被停用了，请联系药剂科");
        }
        if (detailsOfDrugs.getYpLevel() > userInfo.getDoctorLevel()) {
            Integer superiorPhysicianRank = authorizedDoctorLevel.get(item.getSuperiorDoctor(), (code) -> {
                Integer yiShenDengJi = dao.huoQuYiShenDengJi(code);
                return yiShenDengJi == null ? 0 : yiShenDengJi;
            });
            if (detailsOfDrugs.getYpLevel() > superiorPhysicianRank) {
                errorMessage.add("您没有开此药品的权限");
            }
        }
        if (StringUtil.isBlank(detailsOfDrugs.getNationalCode())) {
            warningMessage.add("该药品没有医保编码");
        }
        if (BigUtils.bigXiaoYu(detailsOfDrugs.getStockAmount(), 10)) {
            warningMessage.add(String.format("该药品剩余数量为:【%s】", detailsOfDrugs.getStockAmount().stripTrailingZeros().toPlainString()));
        }
        if (BigUtils.bigDaYu(item.getDrugQuan(), detailsOfDrugs.getStockAmount())) {
            errorMessage.add("药品领量大于药品的库存，当前库存量" + detailsOfDrugs.getStockAmount().stripTrailingZeros().toPlainString());
        }
        //  严格校验
        if (strictVerification) {
            if (detailsOfDrugs.getDeptRestrictions() > 0) {
                errorMessage.add("该药品禁止在患者所在的科室使用。");
            }
            if (detailsOfDrugs.getVisibleFlagZy() == 1) {
                errorMessage.add("该药品禁止住院患者使用。");
            }
            item.setKjywFlag(detailsOfDrugs.getKjywFlag());
            if (detailsOfDrugs.getKjywFlag() == 1) {
                if (item.getYyfs() == null) {
                    errorMessage.add("请填写抗菌药物医嘱附注信息录入");
                } else if (item.getYyfs() == 1 || item.getYyfs() == 2) {
                    if (item.getSsqk() == null) {
                        errorMessage.add("当用药方式为 1 或 2 时，手术切口和用药时间不能为空");
                    }
                }
            }
//            校验医保限制用药(item);
        }
        String 出院带药 = "007";
        if (item.getSupplyCode() != null && 出院带药.equals(item.getSupplyCode())) {
            if (!ONCE.equals(item.getFrequCode())) {
                errorMessage.add("出院带药不能是长期医嘱。");
            }
            item.setDrugOcc(item.getDrugQuan());
        } else {
            // 计算普通药品的领量 durg_quan durg_occ
            calculateDrugAmount(item, detailsOfDrugs);
            if (item.getDrugOcc() == null) {
                errorMessage.add("医嘱领量错误，请重新选择【剂量单位】，或这可能药房剂量维护错误。");
            }
        }
    }


    void 校验医保限制用药(XinZhenYzActOrder item) {
        if (!checkForRestrictedMedication) return;
        // 自费不管
        if ("1".equals(item.getYbSelfFlag())) return;

        ChargeLimitations chargeLimitations = yiBaoLimitation.get(item.getOrderCode(), dao::hasCharge);

        if (chargeLimitations == null) {
            return;
        }

        List<ChargeLimitations> data = dao.getChargeDay(patientInformation.getInpatientNo(),
                patientInformation.getAdmissTimes(),
                patientInformation.getLedgerSn(),
                item.getOrderCode());

        // 已经使用
        int usedAlready = ListUtil.isBlank(data) ? 0 : data.size();
        //可以使用天数
        int canBeUsed = chargeLimitations.getLimitDay() - usedAlready;

        Date latestNumberOfDays = DateUtil.addNaturalDays(item.getStartTime(), canBeUsed);
        int compare = DateUtil.compare(item.getEndTime(), latestNumberOfDays);
        if (item.getFrequCode().equals("ONCE")) {
            item.setEndTime(item.getStartTime());
        }

        if (item.getEndTime() == null) {
            errorMessage.add(StrUtil.format(
                    "医保限制：【{}】天，请设置停止时间，不得超过【{}】",
                    chargeLimitations.getLimitDay(), DateUtil.formatDate(latestNumberOfDays, DateUtil.DEFAULT_PATTERN)));
        } else if (compare > 0) {
            errorMessage.add(StrUtil.format(
                    "医保限制：【{}】天，请设置停止时间，不得超过【{}】",
                    chargeLimitations.getLimitDay(), DateUtil.formatDate(latestNumberOfDays, DateUtil.DEFAULT_PATTERN)));
        }
    }

    private void strictVerificationFunc(@NotNull XinZhenYzActOrder item) {
        if (item.getParentNo() != null && item.getActOrderNo() != null) {
            if (BigUtils.bigXiaoYu(item.getActOrderNo(), item.getParentNo())) {
                errorMessage.add("子医嘱的医嘱号，小于父医嘱号，无法成组。【原因如果子医嘱的医嘱号小于父医嘱，会导致打印乱码】");
            }
        }
        // 获取患者的入院时间
        if (item.getOrderTime() == null) {
            errorMessage.add("医嘱时间不能为空");
        } else if (item.getStartTime() == null) {
            errorMessage.add("开始时间不能为空");
        } else if (patientInformation.getAdmissDate() == null) {
            errorMessage.add("没有查询到患者的入院时间");
        } else if (DateUtil.shiJianDaXiao(item.getStartTime(), patientInformation.getAdmissDate(), "<")) {
            errorMessage.add("开始时间不能在患者入院之前,患者入院时间" + DateUtil.formatDatetime(patientInformation.getAdmissDate()));
        } else if (DateUtil.shiJianDaXiao(item.getStartTime(), item.getOrderTime(), "<") && !userInfo.getDeptCode().equals("1160000")) {
            errorMessage.add("开始时间不能在开医嘱之前");
        }

        if (item.getEndTime() != null) {
            if (ONCE.equals(item.getFrequCode().trim())) {
                item.setEndTime(null);
            } else {
                if (DateUtil.shiJianDaXiao(item.getEndTime(), item.getStartTime(), "<")) {
                    errorMessage.add("结束时间不能在开始时间之前");
                } else {
                    item.setModifier(TokenUtil.getInstance().getTokenUserId());
                }
            }
        } else {
            item.setModifier(null);
        }
    }


    public XinZhenYzActOrder getDrugData(String code) {
        return drug.get(code, (temp) -> dao.drugDataOne(temp, patientInformation != null ? patientInformation.getZkWard() : "", null));
    }

    public XinZhenYzActOrder getDrugData(String code, String supplyCode) {
        return drug.get(code, (temp) -> dao.drugDataOne(temp, patientInformation != null ? patientInformation.getZkWard() : "", supplyCode));
    }

    public static void calculateDrugAmount(XinZhenYzActOrder item, XinZhenYzActOrder feiYongXinXi) {
        if (feiYongXinXi == null) {
            return;
        }
        item.setDrugWeight(feiYongXinXi.getDrugWeight());
        item.setDrugWeightUnit(feiYongXinXi.getDrugWeightUnit());

        item.setDrugVolume(feiYongXinXi.getDrugVolume());
        item.setDrugVolUnit(feiYongXinXi.getDrugWeightUnit());

        String doseUnit = StringUtil.isBlank(item.getDoseUnit()) ? "" : item.getDoseUnit().trim();

        if (StringUtil.notBlank(feiYongXinXi.getDrugWeightUnit()) && doseUnit.equals(feiYongXinXi.getDrugWeightUnit().trim())) {
            item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getDrugWeight(), 2));
        } else if (StringUtil.notBlank(feiYongXinXi.getDrugVolUnit()) && doseUnit.equals(feiYongXinXi.getDrugVolUnit().trim())) {
            item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getDrugVolume(), 2));
        } else if (StringUtil.notBlank(feiYongXinXi.getPackUnit()) && doseUnit.equals(feiYongXinXi.getPackUnit().trim())) {
            item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getPackSize(), 2));
        }
    }

    private void clearErrorMessage() {
        errorMessage.clear();
        warningMessage.clear();
    }

    private void setReturnMap(String orderNo) {
        Map<String, Object> map = new HashMap<>(Capacity.TWO);
        if (ListUtil.notBlank(errorMessage)) {
            List<String> list = new ArrayList<>(errorMessage);
            map.put("error", list);
            passTheAudit = false;
        }
        if (ListUtil.notBlank(warningMessage)) {
            List<String> list = new ArrayList<>(warningMessage);
            map.put("warning", list);
        }
        if (!map.isEmpty()) {
            returnMap.put(orderNo, map);
        }
    }

    private Map<String, Object> getReturnMap() {
        return returnMap;
    }

    public Boolean getFailed() {
        return !passTheAudit;
    }

    public Boolean multipleExclusions() {
        if (repel == null) {
            return false;
        }
        return repel.getCount() > 1;
    }

    public YiZhuCheckData checkForRestrictedMedication() {
        checkForRestrictedMedication = true;
        return this;
    }

}
