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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
import cn.stylefeng.roses.kernel.sys.api.SysUserServiceApi;
import cn.stylefeng.roses.kernel.sys.api.pojo.user.SimpleUserDTO;
import cn.stylefeng.workflow.api.DefinitionApi;
import cn.stylefeng.workflow.api.NodeApi;
import cn.stylefeng.workflow.api.entity.design.Definition;
import cn.stylefeng.workflow.api.entity.design.Node;
import cn.stylefeng.workflow.api.entity.design.NodeEdge;
import cn.stylefeng.workflow.api.entity.instance.RuntimeInstance;
import cn.stylefeng.workflow.api.enums.ApproverNodeTypeEnum;
import cn.stylefeng.workflow.api.enums.NodeTypeEnum;
import cn.stylefeng.workflow.api.pojo.config.node.NodeData;
import cn.stylefeng.workflow.api.pojo.config.node.approver.ApproverConfig;
import cn.stylefeng.workflow.instance.api.InstanceApi;
import cn.stylefeng.workflow.instance.api.pojo.task.RuTaskRequest;
import cn.stylefeng.workflow.instance.modular.mobile.pojo.MobileNodeInfo;
import cn.stylefeng.workflow.instance.modular.mobile.pojo.MobileNodeRequest;
import cn.stylefeng.workflow.instance.modular.mobile.service.MobileFlowService;
import cn.stylefeng.workflow.instance.modular.mobile.sort.WorkflowSorter;
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.RuApproverService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class MobileFlowServiceImpl implements MobileFlowService {

    @Resource
    private NodeApi nodeApi;

    @Resource
    private RuApproverService ruApproverService;

    @Resource
    private InstanceApi instanceApi;

    @Resource
    private SysUserServiceApi sysUserServiceApi;

    @Resource
    private DefinitionApi definitionApi;

    @Resource
    private ApprovalLogService approvalsLogService;

    @Override
    public List<MobileNodeInfo> getList(MobileNodeRequest mobileNodeRequest) {

        List<Node> definitionNodeList = nodeApi.getDefinitionNodeList(mobileNodeRequest.getFlowDefinitionId());

        List<Node> collect = definitionNodeList.stream()
                .filter(i -> i.getNodeType().equals(NodeTypeEnum.USER_APPROVAL.getNodeType()) || i.getNodeType().equals(NodeTypeEnum.COPY_TO.getNodeType()))
                .collect(Collectors.toList());

        List<MobileNodeInfo> mobileNodeInfos = collect.stream().map(this::parseToMobileNodeInfo).collect(Collectors.toList());

        List<MobileNodeInfo> newSortedNodeList = this.sortMobileNodeInfoList(mobileNodeRequest.getFlowDefinitionId(), mobileNodeInfos);

        for (MobileNodeInfo mobileNodeInfo : newSortedNodeList) {
            List<SimpleUserDTO> simpleUserDTOS = this.getNodeConfigPredictUser(mobileNodeRequest, mobileNodeInfo);
            mobileNodeInfo.setPredictUserList(simpleUserDTOS);
        }

        this.fillApprovalLogInfo(mobileNodeRequest.getFlowInstanceId(), newSortedNodeList);

        return newSortedNodeList;
    }

    private List<MobileNodeInfo> sortMobileNodeInfoList(Long definitionId, List<MobileNodeInfo> mobileNodeInfos) {

        List<Node> definitionNodeList = nodeApi.getDefinitionNodeList(definitionId);
        List<NodeEdge> definitionEdgeList = nodeApi.getDefinitionEdgeList(definitionId);

        List<Node> nodes = WorkflowSorter.sortNodes(definitionNodeList, definitionEdgeList);

        List<MobileNodeInfo> newMobileNodeInfos = CollectionUtil.newArrayList();
        for (Node node : nodes) {
            for (MobileNodeInfo mobileNodeInfo : mobileNodeInfos) {
                if (node.getNodeId().equals(mobileNodeInfo.getNodeId())) {
                    newMobileNodeInfos.add(mobileNodeInfo);
                }
            }
        }

        return newMobileNodeInfos;
    }

    private void fillApprovalLogInfo(Long instanceId, List<MobileNodeInfo> mobileNodeInfos) {
        if (instanceId == null) {
            return;
        }

        RuTaskRequest ruTaskRequest = new RuTaskRequest();
        ruTaskRequest.setInstanceId(instanceId);
        List<ApprovalLog> summaryApprovalLog = approvalsLogService.getSummaryApprovalLogContainCopyLog(ruTaskRequest);

        for (MobileNodeInfo mobileNodeInfo : mobileNodeInfos) {
            for (ApprovalLog approvalLog : summaryApprovalLog) {
                if (mobileNodeInfo.getNodeId().equals(approvalLog.getNodeId())) {

                    List<ApprovalLog> currentNodeApprovalLogs = mobileNodeInfo.getCurrentNodeApprovalLogs();
                    if (ObjectUtil.isEmpty(currentNodeApprovalLogs)) {
                        currentNodeApprovalLogs = CollectionUtil.newArrayList();
                        mobileNodeInfo.setCurrentNodeApprovalLogs(currentNodeApprovalLogs);
                    }

                    currentNodeApprovalLogs.add(approvalLog);
                }
            }
        }

        this.addSubmitUser(mobileNodeInfos, summaryApprovalLog);
    }

    private void addSubmitUser(List<MobileNodeInfo> mobileNodeInfos, List<ApprovalLog> summaryApprovalLog) {

        ApprovalLog startLog = summaryApprovalLog.get(0);

        MobileNodeInfo mobileNodeInfo = new MobileNodeInfo();
        mobileNodeInfo.setNodeId(startLog.getNodeId());
        mobileNodeInfo.setNodeName(startLog.getNodeName());
        mobileNodeInfo.setNodeType(NodeTypeEnum.APPLICANT.getNodeType());
        mobileNodeInfo.setApproverNodeType(ApproverNodeTypeEnum.SINGLE.getMode());

        Long processUserId = startLog.getProcessUserId();

        List<SimpleUserDTO> simpleUserDTOS = this.sysUserServiceApi.batchGetUserSimpleInfoList(CollectionUtil.set(false, processUserId));
        mobileNodeInfo.setPredictUserList(simpleUserDTOS);

        mobileNodeInfos.add(0, mobileNodeInfo);
    }

    private MobileNodeInfo parseToMobileNodeInfo(Node node) {
        MobileNodeInfo mobileNodeInfo = new MobileNodeInfo();
        mobileNodeInfo.setNodeId(node.getNodeId());
        mobileNodeInfo.setNodeName(node.getNodeName());
        mobileNodeInfo.setNodeType(node.getNodeType());
        mobileNodeInfo.setShortContent(node.getShortContent());
        mobileNodeInfo.setNodeData(node.getNodeData());

        Integer approverNodeType = node.getNodeData().getApproverConfig().getApproverStrategy().getApproverNodeType();
        mobileNodeInfo.setApproverNodeType(approverNodeType);
        return mobileNodeInfo;
    }

    private Long getSubmitterUserOrgId(Long instanceId) {
        if (instanceId != null) {
            RuntimeInstance instance = instanceApi.getInstanceInfById(instanceId);
            if (instance == null) {
                return null;
            }
            return instance.getApplicantOrgId();
        } else {
            return LoginContext.me().getLoginUser().getCurrentOrgId();
        }
    }

    private Definition getDefinitionInfo(Long flowDefinitionId) {
        return definitionApi.getDefinitionInfo(flowDefinitionId);
    }

    private List<SimpleUserDTO> getNodeConfigPredictUser(MobileNodeRequest mobileNodeRequest, MobileNodeInfo mobileNodeInfo) {

        NodeData nodeData = mobileNodeInfo.getNodeData();
        ApproverConfig approverConfig = nodeData.getApproverConfig();

        Long submitterUserOrgId = this.getSubmitterUserOrgId(mobileNodeRequest.getFlowInstanceId());

        Definition definitionInfo = this.getDefinitionInfo(mobileNodeRequest.getFlowDefinitionId());

        Set<Long> userIdList = ruApproverService.customGetNodeApproverResult(approverConfig, submitterUserOrgId, definitionInfo, mobileNodeRequest.getInstanceVariable());

        return this.sysUserServiceApi.batchGetUserSimpleInfoList(userIdList);
    }

}
