package cn.stylefeng.workflow.event.modular.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.stylefeng.roses.kernel.rule.util.AopTargetUtils;
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.task.RuTask;
import cn.stylefeng.workflow.api.enums.ModelEventTypeEnum;
import cn.stylefeng.workflow.api.enums.NodeEventTypeEnum;
import cn.stylefeng.workflow.api.enums.TaskEventTypeEnum;
import cn.stylefeng.workflow.api.pojo.InstanceContext;
import cn.stylefeng.workflow.api.pojo.config.SixGraphDesign;
import cn.stylefeng.workflow.api.pojo.config.model.FlowGlobalConfig;
import cn.stylefeng.workflow.api.pojo.config.model.ModelEvent;
import cn.stylefeng.workflow.api.pojo.config.model.TaskEvent;
import cn.stylefeng.workflow.api.pojo.config.node.NodeData;
import cn.stylefeng.workflow.api.pojo.config.node.event.NodeEvent;
import cn.stylefeng.workflow.event.api.BaseEventConsumer;
import cn.stylefeng.workflow.event.api.EventPublishApi;
import cn.stylefeng.workflow.event.api.TaskEventConsumer;
import cn.stylefeng.workflow.instance.api.WorkflowStatusApi;
import cn.stylefeng.workflow.log.api.WorkflowLogApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class EventPublisher implements EventPublishApi {

    @Resource
    private WorkflowStatusApi workflowStatusApi;

    @Resource
    private WorkflowLogApi workflowLogApi;

    @Resource
    private DefinitionApi definitionApi;

    @Resource
    private NodeApi nodeApi;

    @Override
    public void publishModelEvent(ModelEventTypeEnum modelEventTypeEnum, Long definitionId) {

        Definition definitionInfo = null;
        try {
            definitionInfo = definitionApi.getDefinitionInfo(definitionId);
        } catch (Exception e) {
            log.error("无法获取到流程定义信息，无法发布事件！", e);
            return;
        }
        List<ModelEvent> modelEventList = this.getDefinitionModelEventList(modelEventTypeEnum, definitionInfo);
        if (ObjectUtil.isEmpty(modelEventList)) {
            return;
        }

        InstanceContext instanceContext = workflowStatusApi.buildInstanceContext(definitionId, null);

        commonPublishModelEvent(instanceContext, modelEventList);
    }

    @Override
    public void publishModelEvent(ModelEventTypeEnum modelEventTypeEnum, InstanceContext instanceContext) {

        if (instanceContext == null) {
            return;
        }
        List<ModelEvent> definitionModelEventList = this.getDefinitionModelEventList(modelEventTypeEnum, instanceContext.getDefinition());
        if (ObjectUtil.isEmpty(definitionModelEventList)) {
            return;
        }

        commonPublishModelEvent(instanceContext, definitionModelEventList);
    }

    @Override
    public void publishNodeEvent(NodeEventTypeEnum nodeEventTypeEnum, Long nodeId, InstanceContext instanceContext) {

        Node nodeInfo = null;
        try {
            nodeInfo = nodeApi.getNodeInfo(nodeId);
        } catch (Exception e) {
            log.error("获取不到节点信息，无法发布流程事件！", e);
            return;
        }

        NodeData nodeData = nodeInfo.getNodeData();
        if (nodeData == null) {
            return;
        }

        List<NodeEvent> nodeEventList = nodeData.getNodeEventList();
        if (ObjectUtil.isEmpty(nodeEventList)) {
            return;
        }

        List<NodeEvent> finalExecuteNodeEvent = nodeEventList.stream().filter(i -> nodeEventTypeEnum.getCode().equals(i.getNodeEventType())).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(finalExecuteNodeEvent)) {
            return;
        }

        Map<String, BaseEventConsumer> totalEventConsumer = SpringUtil.getBeansOfType(BaseEventConsumer.class);
        Collection<BaseEventConsumer> values = totalEventConsumer.values();

        for (NodeEvent nodeEvent : finalExecuteNodeEvent) {
            String actionClass = nodeEvent.getActionClass();
            for (BaseEventConsumer value : values) {

                Object valueTarget = AopTargetUtils.getTarget(value);

                if (valueTarget.getClass().getName().equals(actionClass)) {
                    workflowLogApi.logSuccess(instanceContext, nodeId, "【事件执行】【流程节点】节点ID：" + nodeId);
                    value.consumerAction(nodeId, instanceContext);
                }
            }
        }
    }

    @Override
    public List<String> getActionClasses() {

        Map<String, BaseEventConsumer> timerActionMap = SpringUtil.getBeansOfType(BaseEventConsumer.class);
        if (ObjectUtil.isNotEmpty(timerActionMap)) {
            Collection<BaseEventConsumer> values = timerActionMap.values();
            return values.stream().map(i -> {

                Object valueTarget = AopTargetUtils.getTarget(i);
                return valueTarget.getClass().getName();
            }).collect(Collectors.toList());
        } else {
            return CollectionUtil.newArrayList();
        }
    }

    @Override
    public void publishModelTaskEvent(TaskEventTypeEnum taskEventTypeEnum, List<RuTask> taskList, InstanceContext instanceContext) {
        commonPublishTaskEvent(taskEventTypeEnum, taskList, instanceContext);
    }

    @Override
    public List<String> getTaskActionClasses() {

        Map<String, TaskEventConsumer> taskEventConsumerMap = SpringUtil.getBeansOfType(TaskEventConsumer.class);
        if (ObjectUtil.isNotEmpty(taskEventConsumerMap)) {
            Collection<TaskEventConsumer> values = taskEventConsumerMap.values();
            return values.stream().map(i -> {

                Object valueTarget = AopTargetUtils.getTarget(i);
                return valueTarget.getClass().getName();
            }).collect(Collectors.toList());
        } else {
            return CollectionUtil.newArrayList();
        }
    }

    private void commonPublishModelEvent(InstanceContext instanceContext, List<ModelEvent> list) {

        Map<String, BaseEventConsumer> totalEventConsumer = SpringUtil.getBeansOfType(BaseEventConsumer.class);
        Collection<BaseEventConsumer> values = totalEventConsumer.values();

        for (ModelEvent modelEvent : list) {
            String actionClass = modelEvent.getActionClass();
            for (BaseEventConsumer value : values) {

                Object valueTarget = AopTargetUtils.getTarget(value);

                if (valueTarget.getClass().getName().equals(actionClass)) {
                    workflowLogApi.logSuccess(instanceContext, null, "【事件执行】【流程模型】" + actionClass);
                    value.consumerAction(null, instanceContext);
                }
            }
        }
    }

    public List<ModelEvent> getDefinitionModelEventList(ModelEventTypeEnum modelEventTypeEnum, Definition definition) {

        if (definition == null) {
            return null;
        }
        SixGraphDesign xSixGraphJson = definition.getXSixGraphJson();
        if (xSixGraphJson == null) {
            return null;
        }
        FlowGlobalConfig flowGlobalConfig = xSixGraphJson.getFlowGlobalConfig();
        if (flowGlobalConfig == null) {
            return null;
        }
        List<ModelEvent> modelEventList = flowGlobalConfig.getModelEventList();
        if (ObjectUtil.isEmpty(modelEventList)) {
            return null;
        }

        return modelEventList.stream().filter(modelEvent -> modelEventTypeEnum.getCode().equals(modelEvent.getDefinitionEventType())).collect(Collectors.toList());
    }

    public List<TaskEvent> getTaskEventList(Definition definition) {

        if (definition == null) {
            return null;
        }
        SixGraphDesign xSixGraphJson = definition.getXSixGraphJson();
        if (xSixGraphJson == null) {
            return null;
        }
        FlowGlobalConfig flowGlobalConfig = xSixGraphJson.getFlowGlobalConfig();
        if (flowGlobalConfig == null) {
            return null;
        }
        List<TaskEvent> taskEventList = flowGlobalConfig.getTaskEventList();
        if (ObjectUtil.isEmpty(taskEventList)) {
            return null;
        }

        return taskEventList;
    }

    private void commonPublishTaskEvent(TaskEventTypeEnum taskEventTypeEnum, List<RuTask> taskList, InstanceContext instanceContext) {

        Definition definitionInfo = instanceContext.getDefinition();
        List<TaskEvent> taskEventList = this.getTaskEventList(definitionInfo);
        if (ObjectUtil.isEmpty(taskEventList)) {
            return;
        }

        Map<String, TaskEventConsumer> taskEventConsumerMap = SpringUtil.getBeansOfType(TaskEventConsumer.class);
        Collection<TaskEventConsumer> currentProjectTotalTaskConsumers = taskEventConsumerMap.values();

        for (TaskEvent taskEvent : taskEventList) {
            for (TaskEventConsumer taskEventConsumer : currentProjectTotalTaskConsumers) {

                String actionClass = taskEvent.getActionClass();

                Object valueTarget = AopTargetUtils.getTarget(taskEventConsumer);

                if (valueTarget.getClass().getName().equals(actionClass)) {

                    workflowLogApi.logSuccess(instanceContext, null, "【事件执行】【任务事件】" + actionClass);

                    if (taskEventTypeEnum.equals(TaskEventTypeEnum.SAVE_TASK)) {
                        taskEventConsumer.saveTaskEvent(taskList, instanceContext);
                    } else if (taskEventTypeEnum.equals(TaskEventTypeEnum.DELETE_TASK)) {
                        taskEventConsumer.deleteTaskEvent(taskList, instanceContext);
                    } else if (taskEventTypeEnum.equals(TaskEventTypeEnum.UPDATE_TASK_STATUS)) {
                        taskEventConsumer.updateTaskStatusEvent(taskList.get(0), instanceContext);
                    } else if (taskEventTypeEnum.equals(TaskEventTypeEnum.SET_READ_FLAG)) {
                        taskEventConsumer.updateTaskReadFlagEvent(taskList.get(0), instanceContext);
                    }
                }
            }
        }
    }
}
