package cn.iocoder.yudao.module.bpm.service.task;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.business.BpmBusinessFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.business.BpmBusinessFormMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper;
import cn.iocoder.yudao.module.bpm.enums.BpmBusinessTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyApi;
import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyChangeApi;
import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyRetreatRecordRespVO;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.enums.notice.NotifyPriorityEnum;
import cn.iocoder.yudao.module.system.enums.notice.NotifyTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyInfoReqVO;
import cn.iocoder.yudao.module.system.enums.notice.NotieBusinessTypeEnum;

import javax.annotation.Resource;
import java.util.*;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;

/**
 * 流程抄送 Service 实现类
 *
 * @author kyle
 */
@Service
@Validated
@Slf4j
public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService {

    @Resource
    private BpmProcessInstanceCopyMapper processInstanceCopyMapper;

    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private BpmTaskService taskService;

    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private BpmProcessInstanceService processInstanceService;
    @Resource
    @Lazy // 延迟加载，避免循环依赖
    private BpmProcessDefinitionService processDefinitionService;

    @Resource
    private NotifyMessageSendApi notifyMessageSendApi;

    @Resource
    private BpmBusinessFormMapper bpmBusinessFormMapper;

    @Resource
    private BpmElderlyChangeApi bpmElderlyChangeApi;

    @Resource
    private BpmElderlyApi bpmElderlyApi;

    @Override
    public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String taskId) {
        Task task = taskService.getTask(taskId);
        if (ObjectUtil.isNull(task)) {
            throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
        }
        // 执行抄送
        createProcessInstanceCopy(userIds, reason,
                task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName());
    }

    @Override
    public void createProcessInstanceCopy(Collection<Long> userIds, String reason, String processInstanceId,
                                          String activityId, String activityName, String taskId) {
        // 1.1 校验流程实例存在
        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
        if (processInstance == null) {
            throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
        }
        // 1.2 校验流程定义存在
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
                processInstance.getProcessDefinitionId());
        if (processDefinition == null) {
            throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS);
        }
        //Task task = taskService.getTask(taskId);

        // 2. 创建抄送流程
        List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
                .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
                .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
                .setCategory(processDefinition.getCategory()).setTaskId(taskId)
                .setActivityId(activityId).setActivityName(activityName));
        processInstanceCopyMapper.insertBatch(copyList);
        BpmBusinessFormDO businessFormDO = bpmBusinessFormMapper.selectOne(new LambdaQueryWrapper<BpmBusinessFormDO>()
                .eq(BpmBusinessFormDO::getProcessInstanceId, processInstanceId));
        List<NotifySendSingleToUserReqDTO> copyNotifyList = new ArrayList<>();
        ElderlyInfoReqVO elderlyInfo = null;
        for (int i = 0; i < copyList.size(); i++) {
            NotifySendSingleToUserReqDTO copyDO = new NotifySendSingleToUserReqDTO();
            copyDO.setGotoUrl(getInstanceDetailUrl(processInstanceId) + "&businessId=" + businessFormDO.getBusinessId());
            copyDO.setPkMessage(processInstanceId);
            copyDO.setBusinessType(NotieBusinessTypeEnum.BPM.getValue());
            copyDO.setNotifyType(NotifyTypeEnum.NOTIFY_COPY.getValue());
            copyDO.setTemplateCode(BpmMessageEnum.BPM_COPY_INSTANCE_TASK_ASSIGNED.getTemplateCode());
            copyDO.setPriority(NotifyPriorityEnum.LOW.getValue());
            copyDO.setUserId(copyList.get(i).getUserId());

            Map<String, Object> templateParams = new HashMap<>();
            switch (BpmBusinessTypeEnum.valueOf(businessFormDO.getType())) {
                case APPLY_ACCOMMODATION:
                    elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case APPLY_RETREAT:
                    elderlyInfo = bpmElderlyApi.getElderlyInfoByRetreatId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case BED_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case NURSE_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case CATERING_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case PRICE_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case TRANSFER_CHANGE:
                    break;

                case OUTBOUND_REGISTRATION:
                    break;
                case CHECK_IN:
                    elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;
                default:
                    break;
            }
            templateParams.put("processInstanceName", processInstance.getProcessDefinitionName());
            templateParams.put("taskName", activityName);
            copyDO.setTemplateParams(templateParams);
            copyNotifyList.add(copyDO);
        }
        notifyMessageSendApi.sendSingleMessageToAdminByList(copyNotifyList);
    }

    @Override
    public PageResult<BpmProcessInstanceCopyRespVO> getProcessInstanceCopyPage(Long userId,
                                                                           BpmProcessInstanceCopyPageReqVO pageReqVO) {
        Page<ElderlyRetreatRecordRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        List<BpmProcessInstanceCopyRespVO> list = processInstanceCopyMapper.selectInstanceCopyPage(userId, pageReqVO);
        return new PageResult<>(list, page.getTotal());
    }

    private String getInstanceDetailUrl(String taskId) {
        return "/bpm/process-instance/detail?id=" + taskId;
    }


    @Override
    @Async
    public void createProcessInstanceCopy(Collection<Long> userIds, Task task) {
        if (ObjectUtil.isNull(task)) {
            throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
        }
        // 1.2 校验流程实例存在
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
        if (processInstance == null) {
            throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
        }
        // 1.3 校验流程定义存在
        ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(
                processInstance.getProcessDefinitionId());
        if (processDefinition == null) {
            throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS);
        }

        // 2. 创建抄送流程
        // TODO
        Long tenantId = TenantContextHolder.getTenantId();
        List<BpmProcessInstanceCopyDO> copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO()
                .setUserId(userId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
                .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName())
                .setCategory(processDefinition.getCategory()).setTaskId(task.getId())
                .setTaskName(task.getName()).setTenantId(tenantId));

        processInstanceCopyMapper.insertBatch(copyList);
        BpmBusinessFormDO businessFormDO = bpmBusinessFormMapper.selectOne(new LambdaQueryWrapper<BpmBusinessFormDO>()
                .eq(BpmBusinessFormDO::getProcessInstanceId, processInstanceId));
        List<NotifySendSingleToUserReqDTO> copyNotifyList = new ArrayList<>();
        ElderlyInfoReqVO elderlyInfo = null;
        for (int i = 0; i < copyList.size(); i++) {
            NotifySendSingleToUserReqDTO copyDO = new NotifySendSingleToUserReqDTO();
            copyDO.setGotoUrl(getInstanceDetailUrl(processInstanceId) + "&businessId=" + businessFormDO.getBusinessId());
            copyDO.setPkMessage(processInstanceId);
            copyDO.setBusinessType(NotieBusinessTypeEnum.BPM.getValue());
            copyDO.setNotifyType(NotifyTypeEnum.NOTIFY_COPY.getValue());
            copyDO.setTemplateCode(BpmMessageEnum.BPM_COPY_INSTANCE_TASK_ASSIGNED.getTemplateCode());
            copyDO.setPriority(NotifyPriorityEnum.LOW.getValue());
            copyDO.setUserId(copyList.get(i).getUserId());

            Map<String, Object> templateParams = new HashMap<>();
            switch (BpmBusinessTypeEnum.valueOf(businessFormDO.getType())) {
                case APPLY_ACCOMMODATION:
                    elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case APPLY_RETREAT:
                    elderlyInfo = bpmElderlyApi.getElderlyInfoByRetreatId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case BED_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case NURSE_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case CATERING_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case PRICE_CHANGE:
                    elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                    templateParams.put("elderName", elderlyInfo.getElderName());
                    break;

                case TRANSFER_CHANGE:
                    break;

                case OUTBOUND_REGISTRATION:
                    break;
                default:
                    break;
            }
            templateParams.put("processInstanceName", processInstance.getProcessDefinitionName());
            templateParams.put("taskName", task.getName());
            copyDO.setTemplateParams(templateParams);
            copyNotifyList.add(copyDO);
        }
        notifyMessageSendApi.sendSingleMessageToAdminByList(copyNotifyList);
    }

    @Override
    public void deleteProcessInstanceCopy(String processInstanceId) {
        processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId);
    }

}
