package com.jeelowcode.service.bpm.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.jeelowcode.framework.utils.tool.spring.SpringUtils;
import com.jeelowcode.service.bpm.config.framework.portal.core.PortalRequest;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.PortalTodoResponseDTO;
import com.jeelowcode.service.bpm.config.framework.portal.core.dto.ReceiveCCRequestDTO;
import com.jeelowcode.service.system.api.IApiAdminUserApi;
import com.jeelowcode.service.system.dto.AdminUserRespDTO;
import com.jeelowcode.tool.framework.common.pojo.PageResult;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyCreateReqVO;
import com.jeelowcode.service.bpm.controller.vo.instance.BpmProcessInstanceCopyMyPageReqVO;
import com.jeelowcode.service.bpm.entity.BpmProcessInstanceCopyDO;
import com.jeelowcode.service.bpm.mapper.BpmProcessInstanceCopyMapper;
import com.jeelowcode.service.bpm.enums.ErrorCodeConstants;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfo;
import com.jeelowcode.service.bpm.config.candidate.BpmCandidateSourceInfoProcessorChain;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceCopyService;
import com.jeelowcode.service.bpm.service.IBpmProcessInstanceService;
import com.jeelowcode.service.bpm.service.IBpmTaskService;
import com.jeelowcode.service.bpm.dto.BpmDelegateExecutionDTO;
import com.jeelowcode.tool.framework.common.util.object.ObjectUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static com.jeelowcode.tool.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.jeelowcode.tool.framework.common.util.json.JsonUtils.toJsonString;

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

    @Resource
    private BpmProcessInstanceCopyMapper processInstanceCopyMapper;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private BpmCandidateSourceInfoProcessorChain processorChain;

    @Resource
    @Lazy
    private IBpmTaskService bpmTaskService;
    @Resource
    @Lazy
    private IBpmProcessInstanceService bpmProcessInstanceService;
    @Lazy
    @Resource
    private IApiAdminUserApi apiAdminUserApi;

    @Override
    public boolean makeCopy(BpmCandidateSourceInfo sourceInfo) {
        if (null == sourceInfo) {
            return false;
        }

        Task task = bpmTaskService.getTask(sourceInfo.getTaskId());
        if (ObjectUtil.isNull(task)) {
            return false;
        }
        String processInstanceId = task.getProcessInstanceId();
        if (StrUtil.isBlank(processInstanceId)) {
            return false;
        }
        DelegateExecution executionEntity = new BpmDelegateExecutionDTO(processInstanceId);
        Set<Long> ccCandidates = processorChain.calculateTaskCandidateUsers(executionEntity, sourceInfo);
        if (CollUtil.isEmpty(ccCandidates)) {
            log.warn("相关抄送人不存在 {}", sourceInfo.getTaskId());
            return false;
        } else {
            BpmProcessInstanceCopyDO copyDO = new BpmProcessInstanceCopyDO();
            // 调用
            // 设置任务id
            copyDO.setTaskId(sourceInfo.getTaskId());
            copyDO.setTaskName(task.getName());
            copyDO.setProcessInstanceId(processInstanceId);
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (null == processInstance) {
                log.warn("相关流程实例不存在 {}", sourceInfo.getTaskId());
                return false;
            }
            copyDO.setStartUserId(Long.parseLong(processInstance.getStartUserId()));
            copyDO.setProcessInstanceName(processInstance.getName());
            copyDO.setCategory(null); // TODO 芋艿：貌似新版本，没 processInstance.getProcessDefinitionCategory() 字段？
            copyDO.setReason(sourceInfo.getReason());
            copyDO.setCreator(sourceInfo.getCreator());
            copyDO.setCreateTime(LocalDateTime.now());
            List<BpmProcessInstanceCopyDO> copyList = new ArrayList<>(ccCandidates.size());
            for (Long userId : ccCandidates) {
                BpmProcessInstanceCopyDO copy = BeanUtil.copyProperties(copyDO, BpmProcessInstanceCopyDO.class);
                copy.setUserId(userId);
                copyList.add(copy);
            }
            return processInstanceCopyMapper.insertBatch(copyList);
        }
    }

    @Override
    public void createProcessInstanceCopy(Long userId, BpmProcessInstanceCopyCreateReqVO reqVO) {
        // 1.1 校验任务存在
        Task task = bpmTaskService.getTask(reqVO.getTaskId());
        if (ObjectUtil.isNull(task)) {
            throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
        }
        // 1.2 校验流程存在
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance processInstance = bpmProcessInstanceService.getProcessInstance(processInstanceId);
        if (processInstance == null) {
            log.warn("[createProcessInstanceCopy][任务({}) 对应的流程不存在]", reqVO.getTaskId());
            throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS);
        }

        // 2. 创建抄送流程
        List<BpmProcessInstanceCopyDO> copyList =
                reqVO.getOptions().stream().map(singleUserId ->
                        new BpmProcessInstanceCopyDO()
                                .setTaskId(reqVO.getTaskId()).setTaskName(task.getName())
                                .setProcessInstanceId(processInstanceId).setStartUserId(Long.valueOf(processInstance.getStartUserId()))
                                .setProcessInstanceName(processInstance.getName())
                                .setUserId(singleUserId)
                                .setCategory(null)  // TODO 芋艿：貌似新版本，没 processInstance.getProcessDefinitionCategory() 字段？
                                .setReason(reqVO.getReason())
                ).collect(Collectors.toList());
        processInstanceCopyMapper.insertBatch(copyList);

        // 3.推送给待办系统
        PortalRequest portalRequest = SpringUtils.getBean(PortalRequest.class);
        // 3.1 如果没有配置待办平台，则结束这个监听任务的执行
        if (!Objects.requireNonNull(portalRequest).available()) return;
        // 3.2 调用待办系统，发送待办消息
        try {
            copyList.stream().map(copy -> {
                        AdminUserRespDTO creatorUser = apiAdminUserApi.getUser(Long.parseLong(copy.getCreator()));
                        AdminUserRespDTO receiverUser = apiAdminUserApi.getUser(copy.getUserId());
                        AdminUserRespDTO startUser = apiAdminUserApi.getUser(copy.getStartUserId());
                        return new ReceiveCCRequestDTO()
                                .setFlowId(copy.getProcessInstanceId())
                                .setRequestName(copy.getProcessInstanceName() + "-" + startUser.getNickname() + "-" + DateUtil.formatDateTime(processInstance.getStartTime()))
                                .setWorkflowName(copy.getProcessInstanceName())
                                .setNodeName(copy.getTaskName())
                                .setPcUrl("/process-instance/detail?id=" + copy.getProcessInstanceId())
                                .setCreator(creatorUser.getUsername())
                                .setCreateDateTime(copy.getCreateTime())
                                .setReceiver(receiverUser.getUsername())
                                .setReceiveDateTime(copy.getCreateTime())
                                .setReceiveTs(System.currentTimeMillis());
                    }
            ).forEach(request -> {
                PortalTodoResponseDTO response = portalRequest.receiveCCRequest(request);
                log.info("[taskCompleted][推送待办成功 response({})]", toJsonString(response));
            });
        } catch (Exception e) {
            log.error("[createProcessInstanceCopy][推送待办失败]", e);
        }

    }

    @Override
    public PageResult<BpmProcessInstanceCopyDO> getMyProcessInstanceCopyPage(Long userId, BpmProcessInstanceCopyMyPageReqVO pageReqVO) {
        return processInstanceCopyMapper.selectPage(userId, pageReqVO);
    }

}
