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

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.entity.instance.RuntimeNode;
import cn.stylefeng.workflow.api.entity.task.RuTask;
import cn.stylefeng.workflow.api.enums.TaskTypeEnum;
import cn.stylefeng.workflow.instance.api.RuntimeNodeApi;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.manage.service.RuntimeInstanceService;
import cn.stylefeng.workflow.instance.modular.task.factory.ApprovalLogFactory;
import cn.stylefeng.workflow.instance.modular.task.pojo.ApprovalLog;
import cn.stylefeng.workflow.instance.modular.task.service.ApprovalLogService;
import cn.stylefeng.workflow.instance.modular.task.service.RuTaskService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

@Service
public class ApprovalLogServiceImpl implements ApprovalLogService {

    @Resource
    private RuntimeNodeApi runtimeNodeApi;

    @Resource
    private RuTaskService ruTaskService;

    @Resource
    private RuntimeInstanceService runtimeInstanceService;

    @Resource
    private NodeApi nodeApi;

    @Override
    public List<ApprovalLog> getInstanceApprovalLog(Long instanceId) {
        return this.getInstanceApprovalLogByType(instanceId, false);
    }

    @Override
    public List<ApprovalLog> getInstanceApprovalLogByType(Long instanceId, boolean containCopyLog) {

        RuntimeNode startRuntimeNode = runtimeNodeApi.getStartRuntimeNode(instanceId);
        RuntimeInstance runtimeInstance = runtimeInstanceService.getInstanceInfById(instanceId);
        ApprovalLog startApprovalLog = ApprovalLogFactory.createStartApprovalLog(startRuntimeNode, runtimeInstance.getApplicant());

        List<ApprovalLog> approvalLogList = ListUtil.list(true, startApprovalLog);

        LambdaQueryWrapper<RuTask> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RuTask::getInstanceId, instanceId);

        if (containCopyLog) {
            wrapper.in(RuTask::getTaskType, TaskTypeEnum.HANDLE.getCode(), TaskTypeEnum.PROCESSING.getCode(), TaskTypeEnum.COPY.getCode());
        } else {
            wrapper.in(RuTask::getTaskType, TaskTypeEnum.HANDLE.getCode(), TaskTypeEnum.PROCESSING.getCode());
        }
        wrapper.orderByAsc(RuTask::getTaskId);
        List<RuTask> list = this.ruTaskService.list(wrapper);

        List<ApprovalLog> taskLogs = ApprovalLogFactory.createRuTaskApprovalLog(list);

        approvalLogList.addAll(taskLogs);

        return approvalLogList;
    }

    @Override
    public List<ApprovalLog> getSummaryApprovalLog(RuTaskRequest ruTaskRequest) {

        Long instanceId = ruTaskRequest.getInstanceId();

        List<ApprovalLog> currentLogList = this.getInstanceApprovalLog(instanceId);

        List<ApprovalLog> subFlowApprovalLog = this.getSubFlowApprovalLog(instanceId);

        return this.sortApprovalLogs(currentLogList, subFlowApprovalLog);
    }

    @Override
    public List<ApprovalLog> getSummaryApprovalLogContainCopyLog(RuTaskRequest ruTaskRequest) {

        Long instanceId = ruTaskRequest.getInstanceId();

        List<ApprovalLog> currentLogList = this.getInstanceApprovalLogByType(instanceId, true);

        List<ApprovalLog> subFlowApprovalLog = this.getSubFlowApprovalLog(instanceId);

        return this.sortApprovalLogs(currentLogList, subFlowApprovalLog);
    }

    @Override
    public List<ApprovalLog> getSubFlowApprovalLog(Long instanceId) {

        List<Long> subFlowInstanceIdList = runtimeInstanceService.getSubFlowInstanceIdList(instanceId);
        if (ObjectUtil.isEmpty(subFlowInstanceIdList)) {
            return null;
        }

        ArrayList<ApprovalLog> approvalLogs = new ArrayList<>();
        for (Long subFlowInstanceId : subFlowInstanceIdList) {

            LambdaQueryWrapper<RuTask> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RuTask::getInstanceId, subFlowInstanceId);
            wrapper.eq(RuTask::getTaskType, TaskTypeEnum.PROCESSING.getCode());
            wrapper.orderByDesc(RuTask::getTaskId);
            List<RuTask> list = this.ruTaskService.list(wrapper);
            if (ObjectUtil.isNotEmpty(list)) {
                RuTask finalRuTask = list.get(0);
                ApprovalLog taskLog = ApprovalLogFactory.createApprovalLog(finalRuTask, nodeApi);
                approvalLogs.add(taskLog);
            }
        }

        return approvalLogs;
    }

    private List<ApprovalLog> sortApprovalLogs(List<ApprovalLog> currentLogList, List<ApprovalLog> subFlowApprovalLog) {
        LinkedList<ApprovalLog> approvalLogs = new LinkedList<>();
        if (ObjectUtil.isNotEmpty(currentLogList)) {
            approvalLogs.addAll(currentLogList);
        }
        if (ObjectUtil.isNotEmpty(subFlowApprovalLog)) {
            approvalLogs.addAll(subFlowApprovalLog);
        }


        approvalLogs.sort((o1, o2) -> {

            if (o1 == null || o2 == null) {
                return 0;
            }

            if (o1.getBeginTime() == null || o2.getBeginTime() == null) {
                return 0;
            }

            if (o1.getBeginTime().before(o2.getBeginTime())) {
                return -1;
            }


            else if (o1.getBeginTime().equals(o2.getBeginTime())) {

                if (o1.getApproveTime() == null && o2.getApproveTime() == null) {
                    return 0;
                }

                else if (o1.getApproveTime() == null) {
                    return 1;
                }

                else if (o2.getApproveTime() == null) {
                    return -1;
                }

                else {
                    if (o1.getApproveTime().equals(o2.getApproveTime())) {
                        return 0;
                    } else if (o1.getApproveTime().before(o2.getApproveTime())) {
                        return -1;
                    } else {
                        return 1;
                    }
                }
            }

            else {
                return 1;
            }
        });

        return approvalLogs;
    }
}
