package tech.easyflow.ai.easyagentsflow.listener;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.easyagents.flow.core.chain.*;
import com.easyagents.flow.core.chain.event.*;
import com.easyagents.flow.core.chain.listener.ChainEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.easyagentsflow.event.WorkflowExecutionAuditEvent;
import tech.easyflow.ai.easyagentsflow.event.WorkflowExecutionAuditProducer;
import tech.easyflow.ai.easyagentsflow.repository.FrozenWorkflowDefinitionRegistry;
import tech.easyflow.ai.easyagentsflow.support.PublishedWorkflowDefinitionIds;
import tech.easyflow.ai.easyagentsflow.support.WorkflowExecutionStepKey;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.entity.WorkflowExecResult;
import tech.easyflow.ai.entity.WorkflowExecStep;
import tech.easyflow.ai.service.WorkflowExecResultService;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.utils.WorkFlowUtil;

import javax.annotation.Resource;
import java.util.Date;

@Component
public class ChainEventListenerForSave implements ChainEventListener {


    private static final Logger log = LoggerFactory.getLogger(ChainEventListenerForSave.class);

    @Resource
    private WorkflowService workflowService;
    @Resource
    private WorkflowExecResultService workflowExecResultService;
    @Resource
    private WorkflowExecutionAuditProducer auditProducer;
    @Resource
    private FrozenWorkflowDefinitionRegistry frozenWorkflowDefinitionRegistry;

    @Override
    public void onEvent(Event event, Chain chain) {
        if (event instanceof ChainStartEvent) {
            handleChainStartEvent((ChainStartEvent) event, chain);
        }
        if (event instanceof ChainEndEvent) {
            handleChainEndEvent((ChainEndEvent) event, chain);
        }
        if (event instanceof NodeStartEvent) {
            handleNodeStartEvent((NodeStartEvent) event, chain);
        }
        if (event instanceof NodeEndEvent) {
            handleNodeEndEvent((NodeEndEvent) event, chain);
        }
        if (event instanceof ChainStatusChangeEvent) {
            handleChainStatusChangeEvent((ChainStatusChangeEvent) event, chain);
        }
        if (event instanceof ChainResumeEvent) {
            handleChainResumeEvent((ChainResumeEvent) event, chain);
        }
    }

    private void handleChainStartEvent(ChainStartEvent event, Chain chain) {
        ChainDefinition definition = chain.getDefinition();
        ChainState state = chain.getState();
        log.info(
                "workflow event type=chain-started, instanceId={}, definitionId={}, variableCount={}",
                state.getInstanceId(),
                definition == null ? null : definition.getId(),
                event.getVariables() == null ? 0 : event.getVariables().size());
        Workflow workflow = resolveWorkflow(definition);
        if (workflow == null) {
            log.error("ChainStartEvent: workflow not found, definitionId={}", definition.getId());
            return;
        }
        String instanceId = state.getInstanceId();
        WorkflowExecResult record = new WorkflowExecResult();
        record.setExecKey(instanceId);
        record.setWorkflowId(workflow.getId());
        record.setTitle(workflow.getTitle());
        record.setDescription(workflow.getDescription());
        record.setInput(JSON.toJSONString(event.getVariables()));
        record.setWorkflowJson(workflow.getContent());
        record.setStartTime(new Date());
        record.setStatus(state.getStatus().getValue());
        record.setCreatedKey(WorkFlowUtil.getCreatedKey(chain));
        record.setCreatedBy(WorkFlowUtil.getOperator(chain).getId().toString());
        // 启动记录保留同步确认，避免执行接口返回后立即查询时记录尚不可见。
        try {
            workflowExecResultService.save(record);
        } catch (DuplicateKeyException duplicate) {
            // 重复启动或恢复按 exec_key 幂等处理。
            log.debug("exec result already exists, execKey={}", instanceId, duplicate);
        }
    }

    private void handleChainEndEvent(ChainEndEvent event, Chain chain) {
        ChainState state = chain.getState();
        String instanceId = state.getInstanceId();
        log.info(
                "workflow event type=chain-ended, instanceId={}, status={}",
                instanceId,
                state.getStatus());
        WorkflowExecResult record = new WorkflowExecResult();
        record.setExecKey(instanceId);
        record.setEndTime(new Date());
        record.setStatus(state.getStatus().getValue());
        // 大型引用由审计消费者异步还原，避免阻塞工作流终态提交。
        record.setOutput(JSON.toJSONString(
                state.getExecuteResult()));
        ExceptionSummary error = state.getError();
        if (error != null) {
            record.setErrorInfo(error.getRootCauseClass() + " --> " + error.getRootCauseMessage());
        }
        sendAuditEvent(
                WorkflowExecutionAuditEvent.Type.CHAIN_ENDED,
                instanceId + ":chain-ended",
                instanceId,
                record,
                null);
    }

    private void handleNodeStartEvent(NodeStartEvent event, Chain chain) {
        Node node = event.getNode();
        String auditInstanceId =
                event.getAuditInstanceId();
        ChainState ancestorState =
                StrUtil.isBlank(auditInstanceId)
                        || auditInstanceId.equals(
                        chain.getStateInstanceId())
                        ? chain.getExecutionState()
                        : chain.getChainStateRepository()
                                .load(auditInstanceId);
        if (ancestorState == null) {
            throw new IllegalStateException(
                    "Workflow audit state not found: "
                            + auditInstanceId);
        }

        String instanceId = ancestorState.getInstanceId();
        NodeStatus nodeStatus = event.getStatus();
        if (nodeStatus == null) {
            NodeState nodeState = chain.getNodeState(node.getId());
            nodeStatus = nodeState.getStatus();
        }
        log.info(
                "workflow event type=node-started, instanceId={}, nodeId={}, nodeType={}, status={}",
                instanceId,
                node.getId(),
                node.getClass().getSimpleName(),
                nodeStatus);

        String execKey = currentStepExecKey(
                event.getExecutionAttemptKey(),
                chain,
                node);
        WorkflowExecStep step = new WorkflowExecStep();
        step.setExecKey(execKey);
        step.setNodeId(node.getId());
        step.setNodeName(node.getName());
        // 业务线程保留大型引用，完整审计输入由 MQ 消费线程异步还原。
        step.setInput(JSON.toJSONString(
                ancestorState
                        .resolveParametersPreservingReferences(
                                node)));
        step.setNodeData(JSON.toJSONString(node));
        step.setStartTime(new Date());
        step.setStatus(nodeStatus.getValue());
        sendAuditEvent(
                WorkflowExecutionAuditEvent.Type.NODE_STARTED,
                execKey + ":started",
                instanceId,
                null,
                step);
    }

    private void handleNodeEndEvent(NodeEndEvent event, Chain chain) {
        Node node = event.getNode();
        String auditInstanceId =
                chain.getAuditInstanceId();
        NodeState legacyNodeState = null;
        NodeStatus nodeStatus =
                event.getStatus();
        if (nodeStatus == null) {
            // 兼容旧版引擎未携带不可变终态的事件。
            legacyNodeState =
                    chain.getNodeState(
                            node.getId());
            nodeStatus =
                    legacyNodeState.getStatus();
        }
        log.info(
                "workflow event type=node-ended, instanceId={}, nodeId={}, nodeType={}, status={}, resultFieldCount={}",
                auditInstanceId,
                node.getId(),
                node.getClass().getSimpleName(),
                nodeStatus,
                event.getResult() == null ? 0 : event.getResult().size());
        String execKey = currentStepExecKey(
                event.getExecutionAttemptKey(),
                chain,
                node);
        WorkflowExecStep step = new WorkflowExecStep();
        step.setExecKey(execKey);
        // 节点线程只投递轻量引用，完整执行记录仍由审计消费者透明还原。
        step.setOutput(JSON.toJSONString(
                event.getResult()));
        step.setEndTime(new Date());
        step.setStatus(nodeStatus.getValue());
        ExceptionSummary error =
                event.getError() == null
                        ? (legacyNodeState == null
                        ? null
                        : legacyNodeState.getError())
                        : new ExceptionSummary(
                                event.getError());
        if (error != null) {
            step.setErrorInfo(error.getRootCauseClass() + " --> " + error.getRootCauseMessage());
        }
        sendAuditEvent(
                WorkflowExecutionAuditEvent.Type.NODE_ENDED,
                execKey + ":ended",
                auditInstanceId,
                null,
                step);
    }

    private void handleChainStatusChangeEvent(ChainStatusChangeEvent event, Chain chain) {
        log.info(
                "workflow event type=chain-status-changed, instanceId={}, status={}",
                chain.getStateInstanceId(),
                event.getStatus());
    }

    private void handleChainResumeEvent(ChainResumeEvent event, Chain chain) {
        log.info(
                "workflow event type=chain-resumed, instanceId={}",
                chain.getStateInstanceId());
    }

    /**
     * 根据定义 ID 解析当前执行所对应的工作流。
     * 已发布快照执行会使用 published 前缀，需要先还原为真实工作流 ID。
     */
    private Workflow resolveWorkflow(ChainDefinition definition) {
        if (definition == null || StrUtil.isBlank(definition.getId())) {
            return null;
        }
        String definitionId = definition.getId();
        if (frozenWorkflowDefinitionRegistry.isFrozen(definitionId)) {
            return frozenWorkflowDefinitionRegistry.getWorkflow(definitionId);
        }
        String workflowId = PublishedWorkflowDefinitionIds.unwrap(definitionId);
        try {
            java.math.BigInteger id = new java.math.BigInteger(workflowId);
            if (PublishedWorkflowDefinitionIds.isPublished(definitionId)) {
                return workflowService.getPublishedById(id);
            }
            return workflowService.getById(id);
        } catch (NumberFormatException ex) {
            log.error("Unsupported workflow definition id: {}", definitionId, ex);
            return null;
        }
    }

    /**
     * 生成节点本次业务尝试的稳定执行步骤键。
     *
     * @param capturedAttemptKey 事件创建时捕获的业务尝试键
     * @param chain 当前工作流
     * @param node 当前节点
     * @return 长度固定的执行步骤键
     */
    private String currentStepExecKey(
            String capturedAttemptKey,
            Chain chain,
            Node node) {
        String attemptKey = capturedAttemptKey;
        if (StrUtil.isBlank(attemptKey)) {
            // 兼容旧版引擎直接构造、尚未携带不可变尝试键的事件。
            NodeState nodeState =
                    chain.getNodeState(node.getId());
            attemptKey = nodeState == null
                    ? null
                    : nodeState
                    .getExecutionAttemptKey();
        }
        String execKey =
                WorkflowExecutionStepKey.resolve(
                        attemptKey);
        if (StrUtil.isBlank(execKey)) {
            throw new IllegalStateException(
                    "Workflow execution attempt key is unavailable: " + node.getId());
        }
        return execKey;
    }

    /**
     * 投递工作流执行审计事件。
     *
     * @param type 事件类型
     * @param eventId 事件幂等 ID
     * @param instanceId 顶级工作流实例 ID
     * @param result 工作流执行记录快照
     * @param step 节点执行步骤快照
     */
    private void sendAuditEvent(WorkflowExecutionAuditEvent.Type type,
                                String eventId,
                                String instanceId,
                                WorkflowExecResult result,
                                WorkflowExecStep step) {
        WorkflowExecutionAuditEvent auditEvent = new WorkflowExecutionAuditEvent();
        auditEvent.setType(type);
        auditEvent.setEventId(eventId);
        auditEvent.setInstanceId(instanceId);
        auditEvent.setOccurredAt(new Date());
        auditEvent.setResult(result);
        auditEvent.setStep(step);
        auditProducer.send(auditEvent);
    }
}
