package cn.stylefeng.workflow.instance.modular.task.taskopt.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.team.form.api.FormModelServiceApi;
import cn.stylefeng.team.project.api.FormDataServiceApi;
import cn.stylefeng.team.project.api.pojo.request.BusinessUpdateRequest;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.NodeEventTypeEnum;
import cn.stylefeng.workflow.api.enums.TaskStatusEnum;
import cn.stylefeng.workflow.api.enums.TaskTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.InstanceVariable;
import cn.stylefeng.workflow.event.api.EventPublishApi;
import cn.stylefeng.workflow.instance.api.WorkflowControlApi;
import cn.stylefeng.workflow.instance.api.WorkflowStatusApi;
import cn.stylefeng.workflow.instance.api.pojo.request.WorkflowFinishNodeRequest;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeInstanceService;
import cn.stylefeng.workflow.instance.modular.task.enums.RuTaskExceptionEnum;
import cn.stylefeng.workflow.instance.modular.task.pojo.operate.AgreeTaskRequest;
import cn.stylefeng.workflow.instance.modular.task.pojo.operate.AgreeTaskSimpleRequest;
import cn.stylefeng.workflow.instance.modular.task.service.RuTaskService;
import cn.stylefeng.workflow.instance.modular.task.service.TaskDaoWithEvent;
import cn.stylefeng.workflow.instance.modular.task.strategy.agree.NodePassStrategyContext;
import cn.stylefeng.workflow.instance.modular.task.taskopt.TaskAgreeService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class TaskAgreeServiceImpl implements TaskAgreeService {

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private WorkflowControlApi workflowControlApi;

    @Resource
    private RuntimeInstanceService runtimeInstanceService;

    @Resource
    private FormModelServiceApi formModelServiceApi;

    @Resource
    private FormDataServiceApi formDataServiceApi;

    @Resource
    private NodeApi nodeApi;

    @Resource
    private EventPublishApi eventPublishApi;

    @Resource
    private WorkflowStatusApi workflowStatusApi;

    @Resource
    private TaskDaoWithEvent taskDaoWithEvent;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agree(AgreeTaskRequest agreeTaskRequest) {

        RuTask userWaitDoTask = this.getRuTask(agreeTaskRequest.getTaskId());

        taskDaoWithEvent.updateTaskStatus(userWaitDoTask, TaskStatusEnum.PROCESSED.getCode(), agreeTaskRequest.getSuggestContent());

        this.updateFormData(userWaitDoTask, agreeTaskRequest);

        this.processDoNextNodeAction(userWaitDoTask);
    }

    @Override
    public void agreeNotUpdateVariable(AgreeTaskSimpleRequest agreeTaskSimpleRequest) {

        RuTask userWaitDoTask = this.getRuTask(agreeTaskSimpleRequest.getTaskId());

        taskDaoWithEvent.updateTaskStatus(userWaitDoTask, TaskStatusEnum.PROCESSED.getCode(), agreeTaskSimpleRequest.getSuggestContent());

        this.updatePointUser(userWaitDoTask, agreeTaskSimpleRequest);

        this.processDoNextNodeAction(userWaitDoTask);
    }

    private void updateFormData(RuTask ruTask, AgreeTaskRequest agreeTaskRequest) {

        Long instanceId = ruTask.getInstanceId();
        RuntimeInstance runtimeInstance = this.runtimeInstanceService.getById(instanceId);
        InstanceVariable oldVariable = runtimeInstance.getInstanceVariable();

        if (ObjectUtil.isNotEmpty(agreeTaskRequest.getInstanceVariable())) {

            if (ObjectUtil.isNotEmpty(agreeTaskRequest.getInstanceVariable().getCustomVueFormData())) {
                oldVariable.setCustomVueFormData(agreeTaskRequest.getInstanceVariable().getCustomVueFormData());
            }

            if (ObjectUtil.isNotEmpty(agreeTaskRequest.getInstanceVariable().getFormData())) {
                oldVariable.setFormData(agreeTaskRequest.getInstanceVariable().getFormData());
            }

            if (ObjectUtil.isNotEmpty(agreeTaskRequest.getInstanceVariable().getPointNodeApproverUserId())) {
                Map<Long, List<Long>> oldVariablePointNodeApproverUserId = oldVariable.getPointNodeApproverUserId();
                if (ObjectUtil.isEmpty(oldVariablePointNodeApproverUserId)) {
                    oldVariablePointNodeApproverUserId = new HashMap<>();
                }
                oldVariablePointNodeApproverUserId.putAll(agreeTaskRequest.getInstanceVariable().getPointNodeApproverUserId());
                oldVariable.setPointNodeApproverUserId(oldVariablePointNodeApproverUserId);
            }
            runtimeInstance.setInstanceVariable(oldVariable);
            this.runtimeInstanceService.updateById(runtimeInstance);
        }


        String bizPrimaryKey = runtimeInstance.getBizPrimaryKey();
        if (StrUtil.isBlank(bizPrimaryKey)) {
            return;
        }

        InstanceVariable instanceVariable = agreeTaskRequest.getInstanceVariable();
        if (ObjectUtil.isEmpty(instanceVariable)) {
            return;
        }

        Map<String, Map<String, Object>> formData = instanceVariable.getFormData();
        if (ObjectUtil.isEmpty(formData)) {
            return;
        }

        String tempFormDefinitionId = formData.keySet().iterator().next();
        Long formBusinessId = formModelServiceApi.getFormBusinessId(Convert.toLong(tempFormDefinitionId));
        if (formBusinessId == null) {
            return;
        }

        BusinessUpdateRequest businessUpdateRequest = new BusinessUpdateRequest();
        businessUpdateRequest.setBusinessId(formBusinessId);
        businessUpdateRequest.setPrimaryKeyId(Long.valueOf(bizPrimaryKey));
        businessUpdateRequest.setFormDetail(formData);
        formDataServiceApi.update(businessUpdateRequest);
    }

    private RuTask getRuTask(Long ruTaskId) {

        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(RuTask::getTaskId, ruTaskId);

        queryWrapper.in(RuTask::getTaskType, TaskTypeEnum.HANDLE.getCode(), TaskTypeEnum.PROCESSING.getCode());

        queryWrapper.in(RuTask::getTaskStatus, TaskStatusEnum.WAIT_TO_START.getCode(), TaskTypeEnum.PROCESSING.getCode());

        RuTask userWaitDoTask = this.ruTaskService.getOne(queryWrapper, false);

        if (ObjectUtil.isEmpty(userWaitDoTask)) {
            throw new ServiceException(RuTaskExceptionEnum.CANT_AGREE);
        }

        return userWaitDoTask;
    }

    private LambdaQueryWrapper<RuTask> createSingleWrapper(Integer taskStatus, Integer taskType, Long taskId) {
        LambdaQueryWrapper<RuTask> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(ObjectUtil.isNotNull(taskStatus), RuTask::getTaskStatus, taskStatus);

        queryWrapper.eq(ObjectUtil.isNotNull(taskType), RuTask::getTaskType, taskType);

        queryWrapper.eq(ObjectUtil.isNotEmpty(taskId), RuTask::getTaskId, taskId);

        return queryWrapper;
    }

    private void processDoNextNodeAction(RuTask userWaitDoTask) {
        boolean finishCurrentNodeAndDoNext = NodePassStrategyContext.getAgreeStrategy(userWaitDoTask, nodeApi, false);

        InstanceContext instanceContext = null;
        if (finishCurrentNodeAndDoNext) {

            instanceContext = workflowStatusApi.buildInstanceContext(userWaitDoTask.getInstanceId());
            eventPublishApi.publishNodeEvent(NodeEventTypeEnum.APPROVAL_PASSED, userWaitDoTask.getReNodeId(), instanceContext);

            WorkflowFinishNodeRequest workflowFinishNodeRequest = new WorkflowFinishNodeRequest(userWaitDoTask.getInstanceId(),
                    ListUtil.toList(userWaitDoTask.getRuNodeId()));
            workflowControlApi.finishNode(workflowFinishNodeRequest);
        }
    }

    private void updatePointUser(RuTask ruTask, AgreeTaskSimpleRequest agreeTaskSimpleRequest) {

        Long instanceId = ruTask.getInstanceId();
        RuntimeInstance runtimeInstance = this.runtimeInstanceService.getById(instanceId);
        InstanceVariable oldVariable = runtimeInstance.getInstanceVariable();

        Map<Long, List<Long>> pointNodeApproverUserId = agreeTaskSimpleRequest.getPointNodeApproverUserId();
        if (ObjectUtil.isEmpty(pointNodeApproverUserId)) {
            return;
        }

        Map<Long, List<Long>> oldVariablePointNodeApproverUserId = oldVariable.getPointNodeApproverUserId();
        if (ObjectUtil.isEmpty(oldVariablePointNodeApproverUserId)) {
            oldVariablePointNodeApproverUserId = new HashMap<>();
        }
        oldVariablePointNodeApproverUserId.putAll(pointNodeApproverUserId);
        oldVariable.setPointNodeApproverUserId(oldVariablePointNodeApproverUserId);
        runtimeInstance.setInstanceVariable(oldVariable);
        this.runtimeInstanceService.updateById(runtimeInstance);
    }

}
