package com.docus.server.message.busservice.impl;

import com.alibaba.fastjson.JSONObject;
import com.docus.core.util.FileUtil;
import com.docus.core.util.Func;
import com.docus.core.util.ObjectUtil;
import com.docus.infrastructure.core.exception.BaseException;
import com.docus.infrastructure.core.utils.SpringUtil;
import com.docus.infrastructure.web.api.CommonResult;
import com.docus.infrastructure.web.api.ResultCode;
import com.docus.server.message.busservice.SdBusinessService;
import com.docus.server.message.config.CollectTaskConfig;
import com.docus.server.message.consts.StaticResourceMapping;
import com.docus.server.message.controller.vo.SdRyBloodReportVO;
import com.docus.server.message.converters.WsNurseSubmitMessageConvert;
import com.docus.server.message.converters.WsRecordSubmitMessageConvert;
import com.docus.server.message.dao.TrInpatientOutpatientDao;
import com.docus.server.message.dto.*;
import com.docus.server.message.entity.MaternalInfantRelationship;
import com.docus.server.message.entity.TBasic;
import com.docus.server.message.entity.TrInpatientOutpatient;
import com.docus.server.message.feign.dto.CompensateTasRequest;
import com.docus.server.message.feign.dto.HospitalSubmitNodeLogAddDTO;
import com.docus.server.message.feign.enums.HospitalSubmitNodeEnum;
import com.docus.server.message.feign.service.CollectTaskService;
import com.docus.server.message.feign.service.HospitalSubmitNodeServiceApi;
import com.docus.server.message.mapper.SdRyBloodMapper;
import com.docus.server.message.mapper.SdRyIcuMapper;
import com.docus.server.message.mapper.TBasicMapper;
import com.docus.server.message.rpc.ShunDePeopleRpc;
import com.docus.server.message.rpc.dto.HisZyCyYzDTO;
import com.docus.server.message.rpc.request.HisZyCyYzRequest;
import com.docus.server.message.rpc.response.HisZyCyYzResponse;
import com.docus.server.message.util.TableJsonRead;
import com.docus.server.message.validate.RecordSubmitValidate;
import com.docus.server.message.validate.WsNurseSubmitValidate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SdBusinessServiceImpl implements SdBusinessService {
    @Value("${ip}")
    private String localIp;
    @Value("${server.port}")
    private String port;

    @Autowired
    private CollectTaskService collectTaskService;
    @Autowired
    private TBasicMapper tBasicMapper;
    @Autowired
    private ShunDePeopleRpc shunDePeopleRpc;
    @Autowired
    private SdRyIcuMapper sdRyIcuMapper;
    @Autowired
    private HospitalSubmitNodeServiceApi hospitalSubmitNodeServiceApi;
    @Autowired
    private SdRyBloodMapper sdRyBloodMapper;
    @Resource
    private TrInpatientOutpatientDao trInpatientOutpatientDao;

    private static final TableJsonRead JSON_READ = new TableJsonRead();

    @Override
    public MessageResponse recordSubmitHandle(Message message) {
        try {
            CollectTaskConfig.TaskConfig taskConfig = CollectTaskConfig.getTaskConfig("WS_RECORD_SUBMIT");
            if (taskConfig == null || Func.isEmpty(taskConfig.getCollectorIds())) {
                throw new RuntimeException("任务配置未配置采集器id,WS_RECORD_SUBMIT");
            }
            RecordSubmitDTO recordSubmitDto = WsRecordSubmitMessageConvert.convert(message.getMessage());
            RecordSubmitValidate.verifyRecordSubmitDTO(recordSubmitDto);
            List<String> patientIds = tBasicMapper.getPatientIdsByInpatientNoAndTimes(recordSubmitDto.getInHospIndexNo(), recordSubmitDto.getVisitNo());
            verifyPatientIds(patientIds);

            // 添加提交记录
            String content = "提交医生：" + recordSubmitDto.getSubmitDoctorName() + "," + recordSubmitDto.getSubmitDoctorCode()
                    + "  " + Func.formatDateTime(recordSubmitDto.getSubmitTime());
            HospitalSubmitNodeLogAddDTO nodeLogAddDTO = new HospitalSubmitNodeLogAddDTO();
            nodeLogAddDTO.setPatientId(patientIds.get(0));
            nodeLogAddDTO.setContent(new String[]{content});
            nodeLogAddDTO.setSubmitNodeEnum(HospitalSubmitNodeEnum.EMR);
            nodeLogAddDTO.setCreater("电子病历提交");
            nodeLogAddDTO.setCreaterName("电子病历提交");
            hospitalSubmitNodeServiceApi.addLog(nodeLogAddDTO);

            SpringUtil.getBean(SdBusinessService.class).trInpatientOutPatient(patientIds.get(0));

            if (!isDischargeAndUpdDisDate(patientIds.get(0), recordSubmitDto.getInHospIndexNo(), recordSubmitDto.getVisitNo())) {
                throw new BaseException("此患者未出院！");
            }
            tBasicMapper.insertOrUpdateDoctorSubmitTime(patientIds.get(0), recordSubmitDto.getSubmitTime());
            log.info("补偿电子病历任务 patientIds:{} collectId:{}", patientIds, taskConfig.getCollectorIds());
            verifyHasBabyAndCompensateTask(patientIds.get(0), taskConfig.getBabyCollectorIds(), taskConfig.getHaveBabyQuery());
            compensateTask(patientIds, taskConfig.getCollectorIds());
            tBasicMapper.updateEmrSubmitState(patientIds.get(0), 1);
            return new MessageResponse(ResultCode.SUCCESS.getCode(), success());
        } catch (BaseException baseException) {
            log.error(baseException.getMessage(), baseException);
            // 业务异常，不希望重试，直接消息返回错误
            MessageResponse response = new MessageResponse(ResultCode.FAILED.getCode(), fail(baseException.getMessage()));
            response.setRetry(0);
            return response;
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            // 不可知的错误，希望重试
            MessageResponse response = new MessageResponse(ResultCode.FAILED.getCode(), fail("系统错误"));
            response.setRetry(1);
            return response;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void trInpatientOutPatient(String patientId) {
        if (trInpatientOutpatientDao.exists(patientId)) {
            return;
        }
        String sdRyIndex = tBasicMapper.getSdRyIndex(patientId);
        if (Func.isBlank(sdRyIndex)) {
            log.warn("patientId：{} 没有患者索引！", patientId);
            return;
        }
        List<String> mzReportJxIds = shunDePeopleRpc.getSdRyMzReportJxIds(sdRyIndex);
        if (Func.isEmpty(mzReportJxIds)) {
            log.warn("patientId：{}， 患者索引：{} ，未查询到门诊号！", patientId, sdRyIndex);
            return;
        }

        List<TrInpatientOutpatient> trInpatientOutpatientList = mzReportJxIds.stream()
                .distinct()
                .map(outNO -> {
                    TrInpatientOutpatient outpatient = new TrInpatientOutpatient();
                    outpatient.setOutpatientNo(outNO);
                    outpatient.setPatientId(patientId);
                    return outpatient;
                }).collect(Collectors.toList());
        trInpatientOutpatientDao.deleteTrInpatientOutpatientByInpatientId(patientId);
        trInpatientOutpatientDao.insertOrUpdateTrInpatientOutpatient(trInpatientOutpatientList);
        log.info("patientId：{} 保存门诊号关系成功！", patientId);
    }


    @Override
    public MessageResponse wsNurseSubmitHandle(Message message) {
        try {
            CollectTaskConfig.TaskConfig taskConfig = CollectTaskConfig.getTaskConfig("WS_NURS_SUBMIT");
            if (taskConfig == null || Func.isEmpty(taskConfig.getCollectorIds())) {
                throw new RuntimeException("任务配置未配置采集器id,WS_NURS_SUBMIT");
            }
            WsNurseSubmitDTO nurseSubmitDTO = WsNurseSubmitMessageConvert.convert(message.getMessage());
            WsNurseSubmitValidate.verifyNurseSubmitDTO(nurseSubmitDTO);
            String inHospIndexNo = nurseSubmitDTO.getInHospIndexNo();
            Integer visitNo = nurseSubmitDTO.getVisitNo();
            List<String> patientIds = tBasicMapper.getPatientIdsByInpatientNoAndTimes(inHospIndexNo, visitNo);
            verifyPatientIds(patientIds);

            // 添加提交内容
            HospitalSubmitNodeLogAddDTO nodeLogAddDTO = new HospitalSubmitNodeLogAddDTO();
            nodeLogAddDTO.setPatientId(patientIds.get(0));
            nodeLogAddDTO.setContent(new String[]{nurseSubmitDTO.getDutyNurse(), nurseSubmitDTO.getQcNurse(), Func.formatDateTime(nurseSubmitDTO.getSubmitTime()), nurseSubmitDTO.getFilesCount().toString()});
            nodeLogAddDTO.setSubmitNodeEnum(HospitalSubmitNodeEnum.NURSE);
            nodeLogAddDTO.setCreater("护理提交");
            nodeLogAddDTO.setCreaterName("护理提交");
            hospitalSubmitNodeServiceApi.addLog(nodeLogAddDTO);

            SpringUtil.getBean(SdBusinessService.class).trInpatientOutPatient(patientIds.get(0));

            if (!isDischargeAndUpdDisDate(patientIds.get(0), inHospIndexNo, visitNo)) {
                throw new BaseException("此患者未出院！");
            }
            // 如果是重症患者，如果没有重症报告的话，不能提交
            if (isIcuRecord(inHospIndexNo, visitNo)) {
                // 更新重症标识
                confirmAndUpdIcuRecordState(inHospIndexNo, visitNo);
                if (!hasIcuFile(patientIds.get(0))) {
                    throw new BaseException("此病人为重症患者，重症系统未提交，不允许提交护理，请通知ICU提交重症文书后再提交护理！");
                }
            }

            tBasicMapper.insertOrUpdateNurseSubmitTime(patientIds.get(0), nurseSubmitDTO.getSubmitTime());
            tBasicMapper.insertOrUpdateNurseFileCount(patientIds.get(0), nurseSubmitDTO.getFilesCount());
            tBasicMapper.insertOrUpdateNurse(patientIds.get(0), nurseSubmitDTO.getQcNurse(), nurseSubmitDTO.getDutyNurse());
            log.info("补偿护理任务 patientIds:{} collectId:{}", patientIds, taskConfig.getCollectorIds());
            verifyHasBabyAndCompensateTask(patientIds.get(0), taskConfig.getBabyCollectorIds(), taskConfig.getHaveBabyQuery());
            compensateTask(patientIds, taskConfig.getCollectorIds());
            return new MessageResponse(ResultCode.SUCCESS.getCode(), success());
        } catch (BaseException baseException) {
            log.error(baseException.getMessage(), baseException);
            // 业务异常，不希望重试，直接消息返回错误
            MessageResponse response = new MessageResponse(ResultCode.FAILED.getCode(), fail(baseException.getMessage()));
            response.setRetry(0);
            return response;
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            // 不可知的错误，希望重试
            MessageResponse response = new MessageResponse(ResultCode.FAILED.getCode(), fail("系统错误"));
            response.setRetry(1);
            return response;
        }
    }

    private void verifyHasBabyAndCompensateTask(String parentPatientId, List<String> babyCollectorIds, String haveBabyQuery) {
        try {
            // 是否是产科患者
            if (isHaveBaby(parentPatientId, haveBabyQuery)) {
                // 病案主键查患者基础信息
                TBasic tBasic = tBasicMapper.getTBasicInfo(parentPatientId);
                // 查医院患者索引
                String parentIndex = tBasicMapper.getSdRyIndex(parentPatientId);
                List<String> babyPatientIds = new ArrayList<>();
                // 查母婴关联关系
                List<MaternalInfantRelationship> maternalInfantRelationship = tBasicMapper.getMiRelationByPatientIndexAndAdmissTimes(parentIndex, tBasic.getAdmissTimes().toString());
                // 根据宝宝的患者索引查病案主键，进行补偿,可能多个宝宝
                for (MaternalInfantRelationship infantRelationship : maternalInfantRelationship) {
                    String babyPatientId = tBasicMapper.getPatientIdBySdRyIndex(infantRelationship.getBabyId());
                    babyPatientIds.add(babyPatientId);
                }
                log.info("补偿婴儿任务 patientIds:{} collectId:{}", babyPatientIds, babyCollectorIds);
                compensateTask(babyPatientIds, babyCollectorIds);
            }
        } catch (Exception ex) {
            log.error("判断是否产科与生成婴儿任务错误了，patientId：" + parentPatientId, ex);
        }

    }

    /**
     * 是否是产科的患者
     *
     * @param patientId     病案主键
     * @param haveBabyQuery 产科患者的查询条件，如果为空，直接判定不是产科
     * @return 根据产科患者查询条件，判定是否是产科的患者
     */
    private boolean isHaveBaby(String patientId, String haveBabyQuery) {
        if (Func.isBlank(haveBabyQuery)) {
            return false;
        }
        // 条件带进去如果能查到，就是产科
        return tBasicMapper.isHaveBaby(patientId, haveBabyQuery) > 0;
    }


    /**
     * 根据病案，查找是否有icu来源，未作废的文件数量>0
     *
     * @param patientId 病案主键
     * @return 是否有icu来源，未作废的文件数量>0
     */
    private boolean hasIcuFile(String patientId) {
        String source = "icu";
        return getFileCountByPatientAndSource(patientId, source) > 0;
    }

    /**
     * 根据病案主键和来源，统计未作废的文件数量
     *
     * @param patientId 病案主键
     * @param source    系统/采集来源
     * @return 未作废文件数量
     */
    private int getFileCountByPatientAndSource(String patientId, String source) {
        return tBasicMapper.getFileCountByPatientAndSource(patientId, source);
    }


    @Override
    public void confirmAndUpdIcuRecordState(String inpatientNo, Integer admissTimes) {
        int state = isIcuRecord(inpatientNo, admissTimes) ? 1 : 0;
        tBasicMapper.updIcuRecordState(inpatientNo, admissTimes, state);
    }

    @Override
    public boolean isIcuRecord(String inpatientNo, Integer admissTimes) {
        Integer count = sdRyIcuMapper.countByInpNoAndAdmissTimes(inpatientNo, admissTimes);
        return count > 0;
    }


    @Override
    public List<SdRyBloodReportVO> getBloodView(String inpatientNo, Integer admissTimes) {
        String disDate = tBasicMapper.getDischargeDateByInpatientNoAndTimes(inpatientNo, admissTimes);
        if (Func.isBlank(disDate)) {
            disDate = "18010203";
        }
        String where = getBloodViewWhere();
        // 视图数据
        List<SdRyBloodReportDTO> bloodReportDtoList = sdRyBloodMapper.getByInpatientNoAndAdmissions(inpatientNo, admissTimes, where);
        if (Func.isEmpty(bloodReportDtoList)) {
            return new ArrayList<>();
        }
        List<SdRyBloodReportVO> vos = new ArrayList<>();
        for (SdRyBloodReportDTO dto : bloodReportDtoList) {
            // 查询报告转换本地映射网络请求路径
            byte[] fileBytes = shunDePeopleRpc.getBloodReport(dto.getFileName());
            String filePath = TableJsonRead.currentPath(StaticResourceMapping.STATIC_RESOURCE_PREFIX + File.separator + StaticResourceMapping.BLOOD)
                    + File.separator
                    + disDate
                    + File.separator
                    + inpatientNo + "_" + admissTimes
                    + File.separator
                    + dto.getFileName();
            File file = new File(filePath);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            FileUtil.toFile(new ByteArrayInputStream(fileBytes), file);
            // 与静态资源配置匹配
            String fileUrl = localIp + ":" + port + "/" + StaticResourceMapping.BLOOD + "/" + disDate + "/" + inpatientNo + "_" + admissTimes + "/" + dto.getFileName();
            // 设置返回数据
            SdRyBloodReportVO vo = new SdRyBloodReportVO();
            vo.setFileUrl(fileUrl);
            vo.setAdmissions(dto.getAdmissions());
            vo.setInpatientNo(dto.getInpatientNo());
            vo.setFileDesc(dto.getFileDesc());
            vo.setFileName(dto.getFileName());
            vo.setFileId(dto.getFileId());
            vos.add(vo);
        }
        return vos;
    }

    private static String getBloodViewWhere() {
        try {
            JSONObject jsonObject = JSON_READ.Read("data-config", "blood-view-collect-config.json", JSONObject.class);
            String where = jsonObject.getString("where");
            return where == null ? "" : where;
        } catch (Exception ex) {
            return "";
        }

    }

    public static void main(String[] args) {
        System.out.println(getBloodViewWhere());
    }

    private void compensateTask(List<String> patientIds, List<String> collectorIds) {
        CompensateTasRequest dto = new CompensateTasRequest();
        dto.setPatientIds(patientIds);
        dto.setCollectorIds(collectorIds);
        dto.setPriority(4);
        CommonResult<String> task = collectTaskService.compensateTask(dto);
        if (ResultCode.FAILED.getCode().equals(task.getCode())) {
            String data = task.getData();
            if (Func.isNotBlank(data) && data.contains("出院")) {
                throw new BaseException("该患者为在院状态，请在患者出院后提交！");
            }
            throw new RuntimeException("补偿失败！");
        }
    }

    private void verifyPatientIds(List<String> patientIds) throws BaseException {
        if (Func.isEmpty(patientIds)) {
            throw new BaseException("住院号+住院次数 ，系统无此患者！");
        }
        if (patientIds.size() > 1) {
            throw new BaseException("住院号+住院次数 ，系统中患者有多个！");
        }
    }

    /**
     * 根据病案主键，获得该患者是否出院,如果没出院，是否更新出院时间再判断是否出院
     *
     * @param patientId 病案主键
     * @return 是否出院
     */
    private boolean isDischargeAndUpdDisDate(String patientId, String inpatientNo, Integer admissTimes) {
        // 第一次，出院则直接判断出院
        int count = tBasicMapper.getDischargeCount(patientId);
        if (count > 0) {
            return true;
        }
        // 没有出院，根据住院出院医嘱，更新出院时间再判断
        HisZyCyYzRequest request = new HisZyCyYzRequest();
        request.setVisitNo(inpatientNo);
        request.setVisitTimes(String.valueOf(admissTimes));
        HisZyCyYzResponse response = shunDePeopleRpc.hisZyCyYz(request);
        // 医嘱没查询到，也表示未出院
        if (isNullYzData(response)) {
            return false;
        }
        HisZyCyYzDTO hisZyCyYzDTO = null;

        List<HisZyCyYzDTO> zyCyYzDtos = response.getData();
        HisZyCyYzDTO dto = zyCyYzDtos.get(0);
        if (ObjectUtil.isNotEmpty(dto)) {
            updateDisDeptAndWardCode(patientId, dto);
        }

        // 3 5 状态可更新时间
        List<String> status = Arrays.asList("3", "5");
        for (HisZyCyYzDTO zyCyYzDTO : zyCyYzDtos) {
            if (status.contains(zyCyYzDTO.getOrder_status())) {
                hisZyCyYzDTO = zyCyYzDTO;
                break;
            }
        }
        // 根据3 5 状态未找到医嘱记录，不更新时间，判定为未出院
        if (Objects.isNull(hisZyCyYzDTO)) {
            return false;
        }
        // 更新出院时间
        tBasicMapper.updateDisDateTime(patientId, hisZyCyYzDTO.getStop_time());
        // 再次判断
        int count2 = tBasicMapper.getDischargeCount(patientId);
        return count2 > 0;
    }

    private void updateDisDeptAndWardCode(String patientId, HisZyCyYzDTO dto) {
        String deptCode = dto.getNowdept();
        String deptCodeName = dto.getNowdeptname();
        String wardCode = dto.getWard_code();
        String wardCodeName = dto.getWard_code_name();
        tBasicMapper.updateDisDeptAndWardCode(patientId, deptCode, deptCodeName, wardCode, wardCodeName);
    }

    /**
     * 判断医嘱返回结果是否是空
     *
     * @param response 医嘱返回结果
     * @return 没有医嘱数据放回true
     */
    private boolean isNullYzData(HisZyCyYzResponse response) {
        return Objects.isNull(response) || Func.isEmpty(response.getData()) || Objects.isNull(response.getData().get(0));
    }


    private String success() {
        return "<Response>" +
                "<RetInfo>" +
                "<RetCode>0</RetCode>" +
                "<RetCon>成功</RetCon>" +
                "</RetInfo>" +
                "</Response>";
    }

    private String fail(String message) {
        return "<Response>" +
                "<RetInfo>" +
                "<RetCode>1</RetCode>" +
                "<RetCon>" + message + "</RetCon>" +
                "</RetInfo>" +
                "</Response>";
    }
}
