package tech.easyflow.ai.easyagentsflow.service;

import com.easyagents.flow.core.chain.ChainState;
import com.easyagents.flow.core.chain.ExceptionSummary;
import com.easyagents.flow.core.chain.NodeState;
import com.easyagents.flow.core.chain.NodeStatus;
import com.easyagents.flow.core.chain.repository.ChainStateRepository;
import com.easyagents.flow.core.chain.repository.NodeStateRepository;
import com.easyagents.flow.core.chain.runtime.ChainExecutor;
import com.easyagents.flow.core.code.impl.JavascriptExecutionException;
import org.springframework.stereotype.Component;
import tech.easyflow.ai.easyagentsflow.entity.ChainInfo;
import tech.easyflow.ai.easyagentsflow.entity.NodeInfo;
import tech.easyflow.common.util.StringUtil;
import tech.easyflow.common.web.exceptions.BusinessException;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * 为工作流设计器提供执行状态查询与结果解析能力。
 */
@Component
public class TinyFlowService {

    /**
     * 工作流执行器及其状态仓储入口。
     */
    @Resource
    private ChainExecutor chainExecutor;

    /**
     * 获取工作流及其节点的执行状态。
     *
     * @param executeId 工作流执行实例 ID
     * @param nodes 设计器中的节点列表
     * @return 工作流执行状态
     */
    public ChainInfo getChainStatus(String executeId, List<NodeInfo> nodes) {

        ChainStateRepository chainStateRepository = chainExecutor.getChainStateRepository();
        NodeStateRepository nodeStateRepository = chainExecutor.getNodeStateRepository();

        ChainState chainState = chainStateRepository.load(executeId);
        if (chainState == null) {
            throw new BusinessException(
                    404,
                    404,
                    "工作流执行状态不存在或已过期");
        }
        ChainInfo res = getChainInfo(executeId, chainState);

        if (nodes != null && !nodes.isEmpty()) {
            Map<String, String> resolvedNodeNames =
                    chainExecutor.getInstanceNodeNames(chainState);
            Map<String, String> nodeNames = resolvedNodeNames == null
                    ? Map.of()
                    : resolvedNodeNames;
            for (NodeInfo node : nodes) {
                if (node != null
                        && StringUtil.noText(node.getNodeName())) {
                    node.setNodeName(nodeNames.get(node.getNodeId()));
                }
                processNodeState(executeId, node, chainState, nodeStateRepository);
                res.getNodes().put(node.getNodeId(), node);
            }
        }
        return res;
    }

    /**
     * 使用同一工作流状态快照补充节点状态，避免轮询期间重复读取工作流状态。
     *
     * @param currentExecuteId 工作流执行实例 ID
     * @param node 待补充状态的节点
     * @param currentChainState 当前轮询取得的工作流状态快照
     * @param nodeStateRepository 节点状态仓储
     */
    private void processNodeState(String currentExecuteId,
                                  NodeInfo node,
                                  ChainState currentChainState,
                                  NodeStateRepository nodeStateRepository) {

        NodeState currentNodeState = nodeStateRepository.load(currentExecuteId, node.getNodeId());

        setNodeStatus(node, currentNodeState, currentChainState);
    }

    /**
     * 将工作流状态转换为设计器响应。
     *
     * @param executeId 工作流执行实例 ID
     * @param chainState 工作流状态快照
     * @return 设计器工作流状态
     */
    private ChainInfo getChainInfo(String executeId, ChainState chainState) {
        ChainInfo res = new ChainInfo();
        res.setExecuteId(executeId);
        res.setStatus(chainState.getStatus().getValue());
        ExceptionSummary chainError = chainState.getError();
        if (chainError != null) {
            res.setMessage(formatError(chainError));
        }
        Map<String, Object> executeResult = chainState.getExecuteResult();
        if (executeResult != null && !executeResult.isEmpty()) {
            @SuppressWarnings("unchecked")
            Map<String, Object> resolved = (Map<String, Object>)
                    chainExecutor.resolveResultReferences(executeResult);
            res.setResult(resolved);
        }
        return res;
    }

    /**
     * 将节点状态和节点执行结果写入设计器节点。
     *
     * @param node 设计器节点
     * @param nodeState 节点状态；节点尚未开始执行时可为空
     * @param chainState 工作流状态快照
     */
    private void setNodeStatus(NodeInfo node, NodeState nodeState, ChainState chainState) {
        String nodeId = node.getNodeId();
        // 旧仓储会为未启动节点返回 READY 状态；纯读取仓储返回空时保持相同行为但不产生写入。
        node.setStatus(nodeState == null
                ? NodeStatus.READY.getValue()
                : nodeState.getStatus().getValue());

        if (nodeState != null) {
            ExceptionSummary error = nodeState.getError();
            if (error != null) {
                node.setMessage(formatError(error));
            }
        }

        Map<String, Object> nodeExecuteResult = chainState.getNodeExecuteResult(nodeId);
        if (nodeExecuteResult != null && !nodeExecuteResult.isEmpty()) {
            @SuppressWarnings("unchecked")
            Map<String, Object> resolved = (Map<String, Object>)
                    chainExecutor.resolveResultReferences(nodeExecuteResult);
            node.setResult(resolved);
        }

        // 只有当参数不为空时才覆盖
        if (chainState.getSuspendForParameters() != null) {
            node.setSuspendForParameters(chainState.getSuspendForParameters());
        }
    }

    /**
     * 将执行异常转换为试运行界面可读的错误信息。
     *
     * @param error 持久化的异常摘要
     * @return 可展示的错误信息
     */
    private String formatError(ExceptionSummary error) {
        if (JavascriptExecutionException.class.getName()
                .equals(error.getExceptionClass())
                && StringUtil.hasText(error.getMessage())) {
            return error.getMessage();
        }
        String rootClass = StringUtil.hasText(error.getRootCauseClass())
                ? error.getRootCauseClass()
                : error.getExceptionClass();
        String rootMessage = StringUtil.hasText(error.getRootCauseMessage())
                ? error.getRootCauseMessage()
                : error.getMessage();
        if (StringUtil.noText(rootClass)) {
            return rootMessage;
        }
        if (StringUtil.noText(rootMessage)) {
            return rootClass;
        }
        return rootClass + " --> " + rootMessage;
    }
}
