package thyyxxk.webserver.service.zhuyuanyisheng.emr;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.interceptor.Interceptor;
import com.dtflys.forest.reflection.ForestMethod;
import com.dtflys.forest.utils.ForestDataType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import thyyxxk.webserver.config.envionment.ApiUrl;
import thyyxxk.webserver.config.envionment.ArchiveConfig;
import thyyxxk.webserver.config.envionment.SystemConfig;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.constants.Capacity;
import thyyxxk.webserver.constants.EmrType;
import thyyxxk.webserver.dao.his.LoginDao;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.emr.EmrFolderDao;
import thyyxxk.webserver.dao.his.zhuyuanyisheng.emr.EmrPatientDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.datamodify.YzTemperature;
import thyyxxk.webserver.entity.dictionary.CodeName;
import thyyxxk.webserver.entity.drg.AuxiliaryFillingOfDiagnosis;
import thyyxxk.webserver.entity.fluorescenceTest.FluorescenceSpecimenResult;
import thyyxxk.webserver.entity.login.UserInfo;
import thyyxxk.webserver.entity.medicalinsurance.inpatient.ZyPatientInfo;
import thyyxxk.webserver.entity.outpatient.thmz.MzEmrPatientData;
import thyyxxk.webserver.entity.zhuyuanyisheng.ZyZkList;
import thyyxxk.webserver.entity.zhuyuanyisheng.emr.*;
import thyyxxk.webserver.entity.zhuyuanyisheng.jianyanjiancha.YshYjReq;
import thyyxxk.webserver.entity.zhuyuanyisheng.shoushu.OpRecord;
import thyyxxk.webserver.http.drg.DrgWebApi;
import thyyxxk.webserver.service.PublicServer;
import thyyxxk.webserver.service.hutoolcache.ExtraCache;
import thyyxxk.webserver.service.externalhttp.emr.EmrEditor;
import thyyxxk.webserver.service.hutoolcache.UserCache;
import thyyxxk.webserver.utils.*;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
@RequiredArgsConstructor
public class EmrServer {
    private final EmrPatientDao dao;
    private final EmrEditor emr;
    private final EmrFolderDao folderDao;
    private final PublicServer publicServer;
    private final LoginDao loginDao;
    private final ExtraCache extraCache;
    private final UserCache userCache;
    private static final ArchiveConfig archiveConfig = SpringUtil.getBean(ArchiveConfig.class);

    private static String emrToken = null;
    private static String EMR_URL;

    public static final String THIS_IS_DIR = "This is Dir";
    private final DrgWebApi drgWebApi;
    private final ApiUrl apiUrl;
    private final SystemConfig systemConfig;

    @Value("${forest.variables.emrUrl}")
    public void setUrl(String key) {
        EMR_URL = key;
    }

    public static class EmrInterceptor implements Interceptor<String> {
        @Override
        public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
            request.addHeader("emr-token", emrToken);
        }
    }

    @PostConstruct
    @Scheduled(cron = "* * */12 * * ?")
    public void token() {
        if (EMR_URL == null) {
            return;
        }
        try {
            String URL = EMR_URL.replace("/emr/runtime/api/v1", "");
            String result = HttpRequest.post(URL + "/emr/runtime/api/v1/oauth/token")
                    .header("Authorization", "Basic " + Base64.encode("user:dc71ccfec05b799ad52360c48d504019"))
                    .form("grant_type", "client_credentials").execute().body();
            JSONObject data = JSONObject.parseObject(result);
            emrToken = data.getString("access_token");
        } catch (Exception ignored) {
        }
    }

    /**
     * 获取这个患者这个住院次数的全部电子病历
     *
     * @param patNo 住院号
     * @param times 住院次数
     * @return 数据
     */
    public ResultVo<List<EmrPatientData>> getPatientDataTree(String patNo, Integer times) {
        List<EmrPatientData> list = dao.getPatientData(patNo, times);

        List<EmrPatientData> folder = extraCache.getValue("电子病历文件夹", () -> folderDao.getFolderByName("HOSPITAL"));
        folder = JSONObject.parseArray(JSONObject.toJSONString(folder), EmrPatientData.class);

        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.success(folder);
        }
        folder.addAll(list);
        Map<String, EmrPatientData> folderMap = new HashMap<>();
        List<EmrPatientData> result = new ArrayList<>();

        folder.add(EmrPatientData.builder()
                .id(null)
                .emrDocumentId("noFolder")
                .name("可拖动病历调整到合适文件夹，右键确认排序")
                .emrCategoryCode(THIS_IS_DIR)
                .sort(0)
                .children(new ArrayList<>())
                .parent(null)
                .build());

        for (EmrPatientData item : folder) {
            folderMap.put(item.getEmrDocumentId(), item);
            if (item.getParent() == null) {
                if ("group-category".equals(item.getType()))
                    result.add(item);
            }
        }

        for (EmrPatientData item : folder) {
            String key = item.getParent();
            EmrPatientData parent = folderMap.get(key);
            if (parent != null) {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(item);
            } else {
                if (THIS_IS_DIR.equals(item.getEmrCategoryCode())) {
                    continue;
                }
                EmrPatientData no = folderMap.get("noFolder");
                no.getChildren().add(item);
            }
        }
        result.sort(Comparator.comparing((o) -> o.getSort() == null ? 0 : o.getSort()));
        return ResultVoUtil.success(result);
    }


    public ResultVo<Map<String, List<JSONObject>>> getEmrTree(String type) {
        Map<String, List<JSONObject>> map = new HashMap<>(Capacity.TWO);
        if ("all".equals(type)) {
            map.put("all", wholeHospitalTemplate());
            map.put("dept", getDeptTemplate());
        } else if ("dept".equals(type)) {
            map.put("dept", getDeptTemplate());
        } else if ("hosp".equals(type)) {
            map.put("all", wholeHospitalTemplate());
        }
        return ResultVoUtil.success(map);
    }

    private List<JSONObject> getDeptTemplate() {
        UserInfo user = userCache.getUserInfoByToken();
        JSONArray data = new JSONArray();
        if (ListUtil.notBlank(user.getPartTimeDept())) {
            user.getPartTimeDept().forEach(item -> {
                data.addAll(emr.getDeptList(item));
            });
        }
        data.addAll(emr.getDeptList(user.getDeptCode()));
        return emrToTree(data);
    }

    /**
     * 获取 整个医院模板
     */
    private List<JSONObject> wholeHospitalTemplate() {
        JSONArray data;
        if (StrUtil.isBlank(systemConfig.getEmrZyDeptCode())) {
            data = emr.getEmrTree();
        } else {
            data = emr.getDeptList(systemConfig.getEmrZyDeptCode());
        }
        return emrToTree(data);
    }

    /**
     * 电子病历转成树状图 二次封装
     *
     * @param data 模板数据
     * @return 返回
     */
    public List<JSONObject> emrToTree(JSONArray data) {
        List<JSONObject> tree = new ArrayList<>();
        if (data.isEmpty()) {
            return tree;
        }
        emrArrToTree(data, tree);
        return tree;
    }

    /**
     * 电子病历转成树状图
     *
     * @param data 电子病历
     * @param tree 树
     */
    public static void emrArrToTree(JSONArray data, List<JSONObject> tree) {
        Map<String, JSONObject> map = new HashMap<>(data.size());
        for (int i = 0; i < data.size(); i++) {
            JSONObject item = data.getJSONObject(i);
            if (item.get("parent") == null) {
                tree.add(item);
            }
            map.put(item.getString("_id"), item);
        }

        for (int i = 0; i < data.size(); i++) {
            JSONObject item = data.getJSONObject(i);
            if (map.containsKey(item.getString("parent"))) {
                JSONObject parentList = map.get(item.getString("parent"));
                parentList.computeIfAbsent("children", k -> new JSONArray());
                parentList.getJSONArray("children").add(item);
            }
        }
    }

    /**
     * 获取电子病历的片段
     *
     * @return 树
     */
    public ResultVo<List<JSONObject>> getSnippetTree() {
        JSONArray data = emr.getSnippetByDept(systemConfig.getEmrZyDeptCode());
        List<JSONObject> tree = new ArrayList<>();
        emrArrToTree(data, tree);
        return ResultVoUtil.success(tree);
    }

    /**
     * 提取患者数据元
     *
     * @param param 结构化文档
     */
    public Map<String, Object> extractDataElement(EmrPatientData param) {
        EmrDataExtract emrDataExtract = dao.extractDataSource(param.getEmrCategoryCode());
        if (emrDataExtract == null) {
            return new HashMap<>();
        }
        // 获取 需要提取的数据元
        List<String> strings = JSON.parseArray(emrDataExtract.getDataExtract(), String.class);
        // 获取前端传入的数据元
        Map<String, Object> extractedData = getStringObjectMap(param, strings);
        EmrDataElement emrDataElement = dao.obtainPatientSOriginalData(param.getPatNo(), param.getTimes());
        if (emrDataElement == null) {
            // 创建的病历可以解锁了
            dao.insertDataSource(param.getPatNo(), param.getTimes(), JSON.toJSONString(extractedData));
        } else {
            JSONObject jsonObject = JSONObject.parseObject(emrDataElement.getDataElement());
            jsonObject.putAll(extractedData);
            dao.updatePatientDataSource(param.getPatNo(), param.getTimes(), JSON.toJSONString(jsonObject));
        }
        return extractedData;
    }

    @NotNull
    private static Map<String, Object> getStringObjectMap(EmrPatientData param, List<String> strings) {
        JSONObject elementData = param.getEmrDataElement();
        // 提取到的数据
        Map<String, Object> extractedData = new HashMap<>(strings.size());
        for (String el : strings) {
            if (elementData.containsKey(el)) {
                JSONObject item;
                try {
                    item = elementData.getJSONObject(el);
                } catch (Exception ignore) {
                    throw new RuntimeException("数据元提取失败，但病历保存成功。数据元名称：【" + el + "】，粘贴时请使用（文本粘贴）。");
                }
                Object value = item.get("value");
                if (value != null) {
                    extractedData.put(el, value);
                }
            }
        }
        return extractedData;
    }

    /**
     * 保存电子病历
     *
     * @param param 参数
     * @return 提示
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<Map<String, Object>> insertEmrData(EmrPatientData param) {
        EmrPatientData patientData = dao.getCategoryCodeByDocumentId(param.getEmrDocumentId());
        // TM的新增病历也是用的这个方法，所以要考虑没有id的病历
        if (patientData != null) {
            if (!SignCompleteEnum.INIT.getCode().equals(patientData.getSignComplete())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者完成电子签名无法修改。");
            }
            if (patientData.getDelFlag() == 1) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "病历已删除。");
            }
        }
        JSONObject saveJson = new JSONObject();
        saveJson.put("document", param.getDocumentData());
        try {
            emr.saveDocument(saveJson);
        } catch (Exception e) {
            return ResultVoUtil.fail(ExceptionEnum.EMR_SAVE, "病历保存错误，请重试！" + e.getMessage());
        }

        param.setCreateId(TokenUtil.getInstance().getTokenUserId());
        Integer isUpdated = dao.whetherThereIsAMedicalRecord(param.getEmrDocumentId());
        if (isUpdated != null) {
            dao.updateCreatedTemplate(param);
            insertAFragmentOfTheCourseOfTheDisease(isUpdated, param.getFragment());
        } else {
            // 新增的时候插入数据
            ZyPatientInfo patInfo = publicServer.getPatInfo(param.getPatNo(), param.getTimes());
            param.setReferPhysician(patInfo.getReferPhysician());
            param.setConsultPhysician(patInfo.getConsultPhysician());
            param.setDeptDirector(patInfo.getDeptDirector());
            dao.emrInsertForTheFirstTime(param);
            insertAFragmentOfTheCourseOfTheDisease(param.getId(), param.getFragment());
        }
        Map<String, Object> extractedData;
        try {
            extractedData = extractDataElement(param);
        } catch (Exception e) {
            return ResultVoUtil.fail(ExceptionEnum.EMR_EXTRACT_OBJECTS, e.getMessage());
        }
        return ResultVoUtil.success(extractedData);
    }

    /**
     * 插入病程记录的片段
     *
     * @param id       病程记录
     * @param fragment 片段
     */
    private void insertAFragmentOfTheCourseOfTheDisease(Integer id, List<EmrProgressNote> fragment) {
        if (ListUtil.isBlank(fragment)) {
            return;
        }
        dao.delOldFragment(id);
        List<List<EmrProgressNote>> lists = ListUtils.partition(fragment, 50);
        for (List<EmrProgressNote> list : lists) {
            dao.insertFragment(list, id);
        }
    }

    /**
     * 根据患者的电子病历 id 来删除
     * 这里用的是逻辑删除
     *
     * @param documentId 电子病历id
     * @return 删除电子病历的同时需要删除, 提取到的数据源.
     */
    public ResultVo<List<String>> deletePatientEmrByDocumentId(String documentId) {
        EmrPatientData patientData = dao.getCategoryCodeByDocumentId(documentId);
        if (!SignCompleteEnum.INIT.getCode().equals(patientData.getSignComplete())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "患者完成电子签名无法删除。");
        }

        // 医务部可以自由删除其他医生的病历
        if (!publicServer.needRule(38)) {
            if (!patientData.getCreateId().equals(TokenUtil.getInstance().getTokenUserId())) {
                return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "创建人不是你无法删除。");
            }
        }

        EmrDataExtract emrDataExtract = dao.extractDataSource(patientData.getEmrCategoryCode());
        List<String> strings = new ArrayList<>();
        if (emrDataExtract != null) {
            // 获取 需要提取的数据元
            strings = JSON.parseArray(emrDataExtract.getDataExtract(), String.class);
            // 获取到已经提取的数据
            EmrDataElement emrDataElement = dao.obtainPatientSOriginalData(patientData.getPatNo(), patientData.getTimes());
            // 转 json
            JSONObject jsonObject = JSONObject.parseObject(emrDataElement.getDataElement());
            for (String string : strings) {
                // 删除提取到的数据
                jsonObject.remove(string);
            }
            // 更新患者数据元
            dao.updatePatientDataSource(patientData.getPatNo(), patientData.getTimes(), JSON.toJSONString(jsonObject));
        }
        // 删除病历
        dao.deletePatientEmrByDocumentId(documentId, TokenUtil.getInstance().getTokenUserId());
        return ResultVoUtil.success(strings);
    }


    /**
     * 查询患者是否有指定的病历了
     *
     * @param param 住院号，住院次数，病历编码
     * @return 返回 Boolean
     */
    public ResultVo<Boolean> queryWhetherThePatientHasASpecifiedMedicalRecord(EmrPatientData param) {
        return ResultVoUtil.success(dao.queryWhetherThePatientHasASpecifiedMedicalRecord(param.getPatNo(), param.getTimes(), param.getEmrCategoryCode()) == 1);
    }


    /**
     * 医生诊断热缩
     *
     * @param userCode  医生编码
     * @param code      诊断 code
     * @param tableName 表名
     */
    public void hotSearchSorting(String userCode, String code, String tableName) {
        boolean insert = dao.queryWhetherThereIsHotSearch(userCode, code, tableName) == 0;
        if (insert) {
            dao.hotSearchSorting(userCode, code, tableName);
        } else {
            dao.updateHotSearch(userCode, code, tableName);
        }
    }


    /**
     * 根据文档 id 提交病历
     *
     * @param documentId 文档id
     * @return 返回数据
     */
    public ResultVo<String> submitMedicalRecord(String documentId) {
        String userCode = TokenUtil.getInstance().getTokenUserId();
        String createdId = dao.getDocumentIdCreatedBy(documentId);
        if (userCode.equals(createdId)) {
            dao.updateSubmissionFlag(documentId, userCode);
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "提交成功");
        }
        return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "创建人不一致无法修改。");
    }

    public ResultVo<String> audit(String documentId) {
        String userCode = TokenUtil.getInstance().getTokenUserId();
        int count = dao.updateAuditByDocumentId(documentId, userCode);
        if (count > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "审核成功");
        }
        return ResultVoUtil.success(ExceptionEnum.NO_DATA_EXIST, "操作失败，可能原因，已经审核完成或还未保存。");
    }

    /**
     * 获取电子病历数据
     *
     * @param panNo  住院号
     * @param times  住院次数
     * @param etType 类型
     * @return 返回数据
     */
    public JSONArray getEmrData(String panNo, Integer times, EmrType etType) {
        // 查询这个病历的 唯一 id
        List<String> idList = dao.getDocumentIdByPatietn(panNo, times, etType.getName());
        JSONArray returnArray = new JSONArray();
        if (ListUtil.isBlank(idList)) {
            return returnArray;
        }
        Map<String, JSONObject> surgicalSequencing = new TreeMap<>();

        for (String s : idList) {
            JSONObject jsonObject = emr.getEditorJsonDataByDocumentId(s);
            if (etType.getCode() == EmrType.DIAGNOSIS.getCode()) {
                returnArray.addAll(extractDiagnosis(jsonObject));
            } else if (etType.getCode() == EmrType.OPERATION.getCode()) {
                String key = DateUtil.formatDatetime(jsonObject.getJSONObject("手术日期").getDate("value"));
                surgicalSequencing.put(key, jsonObject);
            }
        }
        if (!surgicalSequencing.isEmpty()) {
            for (Map.Entry<String, JSONObject> item : surgicalSequencing.entrySet()) {
                returnArray.add(item.getValue());
            }
        }
        return returnArray;
    }

    /**
     * 获取患者提取到的数据
     *
     * @param patNo 住院号
     * @param times 次数
     * @return 返回 json
     */
    public JSONObject getEmrPatientData(String patNo, Integer times) {
        String js = dao.getEmrPatientData(patNo, times);
        if (StringUtil.isBlank(js)) {
            return new JSONObject();
        }
        return JSON.parseObject(js);
    }

    /**
     * 获取患者的手术
     *
     * @param patNo 获取住院号
     * @param times 获取住院次数
     * @return 返回数据
     */
    public JSONArray getPatientSurgery(String patNo, Integer times) {
        List<String> idList = dao.getDocumentIdByPatietn(patNo, times, "shoushujilu");
        JSONArray returnArray = new JSONArray();
        if (ListUtil.isBlank(idList)) {
            return returnArray;
        }
        Map<String, JSONObject> surgicalSequencing = new TreeMap<>();
        for (String s : idList) {
            JSONObject jsonObject = emr.getEditorJsonDataByDocumentId(s);
            JSONObject surgeryDate = jsonObject.getJSONObject("手术日期");
            if (null == surgeryDate) {
                continue;
            }
            String key = DateUtil.formatDatetime(surgeryDate.getDate("value"));
            if (StringUtil.isBlank(key)) {
                continue;
            }
            surgicalSequencing.put(key, jsonObject);
        }
        if (!surgicalSequencing.isEmpty()) {
            for (Map.Entry<String, JSONObject> item : surgicalSequencing.entrySet()) {
                returnArray.add(item.getValue());
            }
        }
        return returnArray;
    }

    private JSONArray extractDiagnosis(JSONObject jsonObject) {
        JSONObject diagnosis = jsonObject.getJSONObject("入院诊断");
        if (diagnosis != null) {
            JSONArray diagnosisArr = diagnosis.getJSONArray("value");
            if (diagnosisArr != null) {
                return diagnosisArr;
            }
        }
        return new JSONArray();
    }


    public ResultVo<String> getDrgIntelligentGrouping(String patNo, Integer times) {
        if (StrUtil.isBlank(apiUrl.getDrgWebApi())) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "未找到DRG服务");
        }

        // 获取诊断
        JSONArray diagnosis = getEmrData(patNo, times, EmrType.DIAGNOSIS);
        // 获取手术
        JSONArray operation = getEmrData(patNo, times, EmrType.OPERATION);
        int ledgerSn = publicServer.getLedgerSn(patNo, times);
        // 获取患者数据
        Map<String, String> mapData = dao.drgPatientInfo(patNo, times, ledgerSn);
        if (mapData == null) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "没有查询到患者信息。");
        }

        mapData.put("visit_id", patNo + "_" + times + "_" + ledgerSn);
        mapData.put("bah", patNo + "_" + times);
        for (int i = 0; i < diagnosis.size(); i++) {
            JSONObject item = diagnosis.getJSONObject(i);
            if (i == 0) {
                mapData.put("jbdm", item.getString("code"));
                mapData.put("zyzd", item.getString("name"));
            } else {
                mapData.put("jbdm" + i, item.getString("code"));
            }
        }

        int operationIndex = 1;

        for (int i = 0; i < operation.size(); i++) {
            JSONObject item = operation.getJSONObject(i);
            JSONArray list;
            try {
                list = item.getJSONObject("手术名称").getJSONArray("value");
            } catch (Exception e) {
                continue;
            }

            if (list == null) {
                continue;
            }
            for (int a = 0; a < list.size(); a++) {
                JSONObject valueList = list.getJSONObject(a);
                mapData.put("ssjczbm" + operationIndex, valueList.getString("code"));
                if (operationIndex == 1) {
                    mapData.put("ssjczmc" + operationIndex, valueList.getString("name"));
                }
                operationIndex++;
            }
        }

        AuxiliaryFillingOfDiagnosis data = EntityCopy.Copy(mapData, AuxiliaryFillingOfDiagnosis.class);

        String toJsonStr = JSON.toJSONString(data, SerializerFeature.WriteNullStringAsEmpty);
        return drgWebApi.getDrgIntelligentGrouping(JSON.parseObject(toJsonStr));
    }

    /**
     * 获取患者最大的住院次数
     *
     * @param patNo 住院号
     * @return 返回最大次数
     */
    public ResultVo<Integer> getDischargeTimes(String patNo) {
        Integer times = dao.getMaxTimes(patNo);
        if (times == null) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "没有查询到患者信息。");
        }
        return ResultVoUtil.success(times);
    }

    public ResultVo<JSONObject> getExtractDataElement(String patNo, Integer times) {
        Map<String, String> map = dao.selectEmrDataElement(patNo, times);
        if (map == null) {
            return ResultVoUtil.success();
        } else {
            return ResultVoUtil.success(JSONObject.parseObject(map.get("data_element")));
        }
    }

    public ResultVo<List<CodeName>> getAllWards() {
        return ResultVoUtil.success(loginDao.getAllWards());
    }


    /**
     * 获取患者已经被删除的病历，管理员不需要限制
     *
     * @param patNo 住院号
     * @return 返回
     */
    public ResultVo<List<EmrPatientData>> getDeleteMedicalRecord(String patNo) {
        Integer times = dao.getAdmissTimes(patNo);
        if (times == null) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "出院患者无法恢复病历，如果要恢复请先召回在院。");
        }
        String userCode = TokenUtil.getInstance().getTokenUserId();
        if (publicServer.needRule()) {
            userCode = "";
        }

        return ResultVoUtil.success(dao.selectEmrDeteles(patNo, times, userCode));
    }

    public ResultVo<JSONObject> getInvalidByDocumentId(String id) {
        return ResultVoUtil.success(emr.getInvalidByDocumentIdApi(id));
    }


    /**
     * 恢复电子病历
     *
     * @param documentId 电子病历文档
     * @return 返回提示
     */
    public ResultVo<String> resumeMedicalRecords(String documentId) {
        EmrPatientData data = dao.selectemPatientDataOne(documentId);
        if (data.getDelFlag() == 0) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "病历已经被恢复了请勿重复点击");
        }
        Integer times = dao.getAdmissTimes(data.getPatNo());
        if (times == null) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "出院患者无法恢复病历，如果要恢复请先召回在院。");
        }
        dao.updateDeleteFlag(data.getId());

        TryUtil.ignoreErr(() -> {
            JSONObject param = new JSONObject();
            param.put("_id", documentId);
            param.put("valid", 1);
            emr.resumeMedicalRecords(documentId, param);
        });
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "病历恢复成功。");
    }

    /**
     * 查询出院患者的申请
     *
     * @param flag 1 只查询自己的 2 查询审核的
     * @return 提示
     */
    public ResultVo<List<DischargedEmrModifyApply>> getDisReqEmr(Integer flag) {
        QueryWrapper<?> qw = new QueryWrapper<>();
        if (flag == 1) {
            qw.eq("req_id", TokenUtil.getInstance().getTokenUserId());
        } else if (flag == 2) {
            qw.eq("req_status", 0);
        }
        qw.ge("edit_date", DateUtil.formatDatetime(new Date(), DateUtil.DATE));
        return ResultVoUtil.success(dao.getDisReqEmr(qw));
    }


    public ResultVo<List<DischargedEmrModifyApply>> downloadTheDischargeEdit(String start, String end) {
        return ResultVoUtil.success(dao.selectDisReqEmyDownload(start, end));
    }


    /**
     * 申请出院患者修改
     *
     * @param param 参数
     * @return 提示
     */
    @Transactional(rollbackFor = Exception.class)
    public ResultVo<String> saveDisEmrReq(DischargedEmrModifyApply param) {
        param.setReqId(TokenUtil.getInstance().getTokenUserId());
        int num = dao.deleteRequest(param.getPatNo());
        dao.insertApplicationEmrDis(param);
        if (num > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "修改原申请成功。");
        } else {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "申请成功。");
        }
    }

    /**
     * 删除申请
     *
     * @param patNo 住院号
     * @return 提示
     */
    public ResultVo<String> deleteDisEmrDis(String patNo) {
        dao.deleteRequest(patNo);
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "操作成功。");
    }

    /**
     * 通过申请
     *
     * @param patNo 住院号
     * @return 提示
     */
    public ResultVo<String> adoptEmrDisReq(String patNo) {
        int num = dao.adoptEmrDisReq(patNo, null, 1);
        if (num > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
        }
        return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, "操作失败，原申请可能已经被删除了。");
    }

    /**
     * 拒绝出院患者申请
     *
     * @param patNo       住院号
     * @param reviewNotes 审核信息
     * @return 提示
     */
    public ResultVo<String> refuseEmrDisReq(String patNo, String reviewNotes) {
        int num = dao.adoptEmrDisReq(patNo, reviewNotes, 2);
        if (num > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
        }
        return ResultVoUtil.fail(ExceptionEnum.INTERNAL_SERVER_ERROR, "操作失败，原申请可能已经被删除了。");
    }


    /**
     * 查询出院患者是否已经通过了编辑
     *
     * @param patNo 住院号
     * @return Boolean
     */
    public ResultVo<Boolean> isDisReqEdit(String patNo) {
        QueryWrapper<?> qw = new QueryWrapper<>();
        qw.eq("pat_no", patNo);
        qw.eq("req_status", 1);
        qw.ge("edit_date", DateUtil.formatDatetime(new Date(), DateUtil.DATE));
        List<DischargedEmrModifyApply> list = dao.getDisReqEmr(qw);
        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.success(false);
        } else {
            return ResultVoUtil.success(true);
        }
    }

    public ResultVo<Object> getHistory(String documentId) {
        return ResultVoUtil.success(emr.getHistory(documentId));
    }

    public ResultVo<List<MedicalHistoryPrompts>> getEmrTips() {
        return ResultVoUtil.success(dao.selectEmrTips(TokenUtil.getInstance().getTokenUserId()));
    }


    public ResultVo<String> addEmrTips(MedicalHistoryPrompts param) {
        UserInfo userInfo = userCache.getUserInfoByToken();
        param.setDept(userInfo.getDeptCode());
        param.setCreatorId(userInfo.getCode());
        dao.insertEmrTips(param);
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
    }

    public ResultVo<String> updateEmrTips(MedicalHistoryPrompts param) {
        if (param.getId() == null) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "id 为空无法更新。");
        }
        dao.updateEmrTips(param);
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
    }

    public ResultVo<String> deleteEmrTips(Integer id) {
        dao.delEmrTips(id);
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
    }

    /**
     * 在病程记录中获取文本数据
     *
     * @param name 名称
     * @return 返回数据
     */
    public ResultVo<List<MedicalHistoryPrompts>> getEmrTipsData(String name, int typeCode) {
        QueryWrapper<?> qw = new QueryWrapper<>();
        UserInfo us = userCache.getUserInfoByToken();
        if (typeCode == 1) {
            qw.eq("creator_id", us.getCode());
        } else if (typeCode == 2) {
            qw.eq("dept", us.getDeptCode());
        }
        qw.like("query_key", name)
                .eq("type_code", typeCode)
                .orderByDesc("count");
        return ResultVoUtil.success(dao.getEmrTipsData(qw));
    }


    public ResultVo<List<ZyPatientInfo>> getListOfDischargedPatients(String patNo) {
        List<ZyPatientInfo> list = dao.getListOfDischargedPatients(patNo);
        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.fail(ExceptionEnum.NULL_POINTER, "没有" + patNo + "的出院记录。");
        }
        return ResultVoUtil.success(list);
    }


    public ResultVo<JSONObject> getYzTemperature(YzTemperatureParam param) {
        JSONObject js = new JSONObject();

        QueryWrapper<?> qw = new QueryWrapper<>();
        qw.eq("inpatient_no", param.getPatNo())
                .eq("admiss_times", param.getTimes())
                .ge("CONVERT(varchar(10), rec_date, 23)", param.getStartTime())
                .le("CONVERT(varchar(10), rec_date, 23)", param.getEndTime());

        List<YzTemperature> list = dao.getYzTemperature(qw);
        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.success(js);
        }
        Map<Date, YzTemperature> map = new LinkedHashMap<>(list.size());
        for (YzTemperature item : list) {
            Date key = item.getTempDate();
            if (map.containsKey(key)) {
                YzTemperature yz = map.get(key);
                yz.setOtherInfo(nullToEmpty(yz.getOtherInfo()) + nullToEmpty(item.getOtherInfo()));
            } else {
                map.put(key, item);
            }
        }
        js.put("list", list);
        js.put("map", map);
        return ResultVoUtil.success(js);
    }

    public ResultVo<List<YshYjReq>> getExamine(String patNo, Integer times) {
        return ResultVoUtil.success(dao.getExamine(patNo, times));
    }

    private String nullToEmpty(String str) {
        if (StringUtil.isBlank(str)) {
            return "";
        }
        return str;
    }

    public ResultVo<ZyZkList> whetherItExistsInTheDepartment(String patNo, Integer times) {
        ZyZkList zk = dao.getMaxTransferDateByPatNo(patNo, times);
        if (zk != null) {
            zk.setTimeout(DateUtil.moreThanHours(zk.getOpTime(), 48));
        }
        return ResultVoUtil.success(zk);
    }

    public ResultVo<String> electronicMedicalRecordSequencing(List<EmrPatientData> data) {
        if (ListUtil.isBlank(data)) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "请选择有效的分类数据。");
        }
        ListUtil.batchList(data, EmrPatientDao.class, (m, item) -> {
            m.updateTheNode(item.getParent(), item.getId());
        });
        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "操作成功。");
    }

    public ResultVo<Map<String, List<EmrPatientData>>> getPastHistory(String patNo, Integer times) {
        List<EmrPatientData> list = dao.getHisPatInfo(patNo, times);

        if (ListUtil.isBlank(list)) {
            return ResultVoUtil.success(new HashMap<>());
        }

        Map<String, List<EmrPatientData>> grouping = list.stream().collect(
                Collectors.groupingBy(item -> item.getTimes().toString())
        );
        return ResultVoUtil.success(grouping);
    }

    public ResultVo<Map<Integer, List<MzEmrPatientData>>> getMzPatientDataByPatNo(String patNo) {
        String mzNo = dao.getMzNoByPatNo(patNo);
        Map<Integer, List<MzEmrPatientData>> map1 = new HashMap<>();
        if (StringUtil.isBlank(mzNo)) {
            return ResultVoUtil.success(map1);
        }
        List<MzEmrPatientData> mzList = dao.getEmrPatientMz(mzNo);
        if (ListUtil.isBlank(mzList)) {
            return ResultVoUtil.success(map1);
        }
        Map<Integer, List<MzEmrPatientData>> map = mzList.stream().collect(
                Collectors.groupingBy(MzEmrPatientData::getTimes)
        );
        return ResultVoUtil.success(map);
    }

    public ResultVo<List<FluorescenceSpecimenResult>> getFluorescenceSpecimenResult(String patNo, Integer times) {
        return ResultVoUtil.success(dao.selectFluorescenceSpecimenResult(patNo, times));
    }

    public ResultVo<List<OpRecord>> getOpRecordList(String patNo, Integer times) {
        return ResultVoUtil.success(dao.getOpRecordList(patNo, times));
    }

    public String getEmrToken() {
        return emrToken;
    }

    private static String createEmr(JSONObject param) {
        if (StrUtil.isBlank(archiveConfig.getArchiveUrl())) {
            throw new RuntimeException("电子病历生成路径为空");
        }

        param.put("emrToken", emrToken);

        String text = Forest.post(archiveConfig.getEmrArchiveUrl())
                .contentTypeJson()
                .addBody(param)
                .bodyType(ForestDataType.JSON)
                .addHeader("emr-token", emrToken)
                .execute(String.class);
        int index = text.lastIndexOf("<br/>buffer:,");
        if (index != -1) {
            String[] split = text.substring(text.lastIndexOf("<br/>buffer:,") + 13).split(",");
            return Base64.encode(Convert.toPrimitiveByteArray(split));
        }
        throw new RuntimeException(text);
    }

    public static String getPdfBase(String documentId) {
        JSONObject param = new JSONObject();
        param.put("emrToken", true);
        param.put("stream", true);
        param.put("arcive", false);
        param.put("patientId", false);
        param.put("params", new JSONObject() {{
            put("documentId", documentId);
        }});
        param.put("fileName", "病历pdf");
        param.put("type", "document");
        return createEmr(param);
    }

    public static String getPdfBase64ByTemplate(cn.hutool.json.JSONObject template, Object data) {
        JSONObject param = new JSONObject();
        param.put("emrToken", true);
        param.put("stream", true);
        param.put("arcive", false);
        param.put("patientId", false);
        param.put("params", new JSONObject() {{
            put("document", template);
            put("appContext", data);
        }});
        param.put("fileName", "病历pdf");
        param.put("type", "template");
        return createEmr(param);
    }


    public static String getPdfBase64ByCategoryId(String id, String categoryCode, Object data) {
        JSONObject param = new JSONObject();
        param.put("emrToken", true);
        param.put("stream", true);
        param.put("arcive", false);
        param.put("patientId", false);
        param.put("params", new JSONObject() {{
            put("categoryId", id);
            put("categoryCode", categoryCode);
            put("appContext", new JSONObject() {{
                put("data", data);
            }});
        }});
        param.put("fileName", "病历pdf");
        param.put("type", "template");
        return createEmr(param);
    }


    public ResultVo<String> newDir(EmrPatientData data) {
        data.setEmrDocumentId(SnowFlakeId.instance().nextId());
        data.setCreateDate(new Date());
        data.setCreateId(TokenUtil.getInstance().getTokenUserId());
        data.setEmrCategoryCode(THIS_IS_DIR);
        if (data.getParent() == null) {
            return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, "父节点为空请重新创建!");
        }
        dao.insert(data);
        return ResultVoUtil.success();
    }

    public ResultVo<String> delDir(String dirId) {
        boolean dirHasFile = dao.dirHasFile(dirId);
        if (dirHasFile) {
            return ResultVoUtil.fail(ExceptionEnum.LOGICAL_ERROR, "文件夹下面还存在文件无法删除。");
        }
        int i = dao.delDir(dirId, TokenUtil.getInstance().getTokenUserId());
        if (i > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
        }
        return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, "无法删除，创建人不是你，或者这个是文件。");
    }

    public ResultVo<String> rename(String name, String documentId) {
        int i = dao.rename(name, documentId);
        if (i > 0) {
            return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE);
        }
        return ResultVoUtil.success(ExceptionEnum.LOGICAL_ERROR, "重命名失败。");
    }

    public String findEmrByCode(String patNo, Integer times, String code) {
        return dao.findEmrByCode(patNo, times, code);
    }

}
