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

import com.docus.core.util.DateUtil;
import com.docus.core.util.Func;
import com.docus.infrastructure.core.exception.BaseException;
import com.docus.infrastructure.redis.service.IdService;
import com.docus.infrastructure.web.api.CommonResult;
import com.docus.infrastructure.web.api.ResultCode;
import com.docus.server.message.busservice.ZqBusinessService;
import com.docus.server.message.config.CollectTaskConfig;
import com.docus.server.message.converters.ZqWsNurseSubmitMessageConvert;
import com.docus.server.message.converters.ZqWsRecordSubmitMessageConvert;
import com.docus.server.message.dto.Message;
import com.docus.server.message.dto.MessageResponse;
import com.docus.server.message.dto.TbasicQrPreciseCondition;
import com.docus.server.message.dto.ZqRecordSubmitDTO;
import com.docus.server.message.dto.ZqWsNurseSubmitDTO;
import com.docus.server.message.entity.DelayedMessages;
import com.docus.server.message.enums.TaskWait;
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.DelayedMessagesMapper;
import com.docus.server.message.mapper.TBasicMapper;
import com.docus.server.message.validate.ZqRecordSubmitValidate;
import com.docus.server.message.validate.ZqWsNurseSubmitValidate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 肇庆业务实现类
 *
 * @author YongBin Wen
 * @date 2024/1/22 13:58
 */
@Slf4j
@Service("ZqBusinessService")
public class ZqBusinessServiceImpl implements ZqBusinessService {

    @Resource
    private TBasicMapper tBasicMapper;

    @Resource
    private HospitalSubmitNodeServiceApi hospitalSubmitNodeServiceApi;

    @Resource
    private CollectTaskService collectTaskService;

    @Resource
    private DelayedMessagesMapper delayedMessagesMapper;

    @Resource
    private IdService idService;

    private final ReentrantLock SUBMIT_LOCK = new ReentrantLock();

    @Override
    public MessageResponse recordSubmitHandle(Message message) {
        SUBMIT_LOCK.lock();
        try {
            CollectTaskConfig.TaskConfig taskConfig = CollectTaskConfig.getTaskConfig("WS_RECORD_SUBMIT");
            if (taskConfig == null || Func.isEmpty(taskConfig.getCollectorIds())) {
                throw new RuntimeException("任务配置未配置采集器id,WS_RECORD_SUBMIT");
            }
            ZqRecordSubmitDTO recordSubmitDto = ZqWsRecordSubmitMessageConvert.convert(message.getMessage());
            ZqRecordSubmitValidate.verifyRecordSubmitDTO(recordSubmitDto);
            TbasicQrPreciseCondition condition = ZqWsRecordSubmitMessageConvert.convert(recordSubmitDto);
            List<String> patientIds = tBasicMapper.getPatientIdByPreciseConditions(condition);
            verifyPatientIds(patientIds);

            String submitDate = recordSubmitDto.getSubmitDate();

            // 添加提交记录
            HospitalSubmitNodeLogAddDTO nodeLogAddDTO = new HospitalSubmitNodeLogAddDTO();
            nodeLogAddDTO.setPatientId(patientIds.get(0));
            nodeLogAddDTO.setContent(new String[]{submitDate});
            nodeLogAddDTO.setSubmitNodeEnum(HospitalSubmitNodeEnum.EMR);
            nodeLogAddDTO.setCreater("电子病历提交");
            nodeLogAddDTO.setCreaterName("电子病历提交");
            hospitalSubmitNodeServiceApi.addLog(nodeLogAddDTO);

            tBasicMapper.insertOrUpdateDoctorSubmitTime(patientIds.get(0), Func.parseDate(submitDate, DateUtil.PATTERN_DATETIME));

            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;
        }finally {
            SUBMIT_LOCK.unlock();
        }
    }

    @Override
    public MessageResponse wsNurseSubmitHandle(Message message) {
        SUBMIT_LOCK.lock();
        try {
            CollectTaskConfig.TaskConfig taskConfig = CollectTaskConfig.getTaskConfig("WS_NURS_SUBMIT");
            if (taskConfig == null || Func.isEmpty(taskConfig.getCollectorIds())) {
                throw new RuntimeException("任务配置未配置采集器id,WS_NURS_SUBMIT");
            }
            ZqWsNurseSubmitDTO nurseSubmitDTO = ZqWsNurseSubmitMessageConvert.convert(message.getMessage());
            ZqWsNurseSubmitValidate.verifyNurseSubmitDTO(nurseSubmitDTO);
            TbasicQrPreciseCondition condition = ZqWsNurseSubmitMessageConvert.convert(nurseSubmitDTO);
            List<String> patientIds = tBasicMapper.getPatientIdByPreciseConditions(condition);
            verifyPatientIds(patientIds);

            String submitDate = nurseSubmitDTO.getSubmitDate();
            // 添加提交内容
            HospitalSubmitNodeLogAddDTO nodeLogAddDTO = new HospitalSubmitNodeLogAddDTO();
            nodeLogAddDTO.setPatientId(patientIds.get(0));
            nodeLogAddDTO.setContent(new String[]{submitDate});
            nodeLogAddDTO.setSubmitNodeEnum(HospitalSubmitNodeEnum.NURSE);
            nodeLogAddDTO.setCreater("护理提交");
            nodeLogAddDTO.setCreaterName("护理提交");
            hospitalSubmitNodeServiceApi.addLog(nodeLogAddDTO);

            tBasicMapper.insertOrUpdateNurseSubmitTime(patientIds.get(0), Func.parseDate(submitDate, DateUtil.PATTERN_DATETIME));

            int delaySeconds = taskConfig.getDelaySeconds();
            List<String> collectorIds = taskConfig.getCollectorIds();
            if (delaySeconds > 0) {
                addDelayTask(patientIds.get(0), collectorIds, delaySeconds);
            } else {
                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;
        }finally {
            SUBMIT_LOCK.unlock();
        }
    }

    private void addDelayTask(String patientId, List<String> collectorIds, int delaySeconds) {
        LocalDateTime nextExecuteDateTime = LocalDateTime.now().plusSeconds(delaySeconds);
        String formatNextExecuteDateTime = Func.formatDateTime(nextExecuteDateTime);
        Date nextExecuteDate = Func.parseDate(formatNextExecuteDateTime, DateUtil.PATTERN_DATETIME);

        for (String collectorId : collectorIds) {
            String key = patientId + "-" + collectorId;
            DelayedMessages messageCondition = new DelayedMessages();
            messageCondition.setKeyword(key);
            messageCondition.setMessageType(TaskWait.SUBMIT_TASK_WAIT_DELAY_MSG_TYPE);
            DelayedMessages delayedMessages = delayedMessagesMapper.findMessage(messageCondition);
            if (Objects.isNull(delayedMessages)) {
                delayedMessages = new DelayedMessages();
                delayedMessages.setId(idService.getDateSeq());
                delayedMessages.setMessageType(TaskWait.SUBMIT_TASK_WAIT_DELAY_MSG_TYPE);
                delayedMessages.setKeyword(key);
                delayedMessages.setMessage(key);
                delayedMessages.setDelaySeconds(delaySeconds);
                delayedMessages.setNextExecutionTime(nextExecuteDate);
                delayedMessages.setCreateTime(new Date());
                delayedMessages.setRetries(0);
                delayedMessages.setErrorMessage("");
                delayedMessagesMapper.insert(delayedMessages);
            } else {
                delayedMessages.setDelaySeconds(delaySeconds);
                delayedMessages.setNextExecutionTime(nextExecuteDate);
                delayedMessagesMapper.updateById(delayedMessages);
            }
        }
    }

    private void verifyPatientIds(List<String> patientIds) {
        if (Func.isEmpty(patientIds)) {
            throw new BaseException("系统无此患者！");
        }
        if (patientIds.size() > 1) {
            throw new BaseException("系统中患者有多个,无法确认！");
        }
    }

    private void compensateTask(String source,List<String> patientIds, List<String> collectorIds) {
        log.info("{},补偿任务 patientIds:{} collectId:{}",source, patientIds, collectorIds);
        CompensateTasRequest dto = new CompensateTasRequest();
        dto.setPatientIds(patientIds);
        dto.setCollectorIds(collectorIds);
        dto.setPriority(3);
        CommonResult<String> result = collectTaskService.compensateTask(dto);
        log.info("{}，补偿任务结果：{}", source,Func.toJson(result));
    }

    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>";
    }
}
