import { Position } from "../xyflow/react.js";
import { ResourceNodeType } from "../components/AgentCanvas/AgentFlow.constants.js";
import { isAgentFlowResourceNode } from "../types.js";
import { autoArrangeNodes } from "./auto-layout.js";
const SPAN_STATUS = {
    RUNNING: 0,
    SUCCESS: 1,
    ERROR: 2
};
const NODE_ID_DELIMITER = '=>';
const EDGE_ID_DELIMITER = '::';
const getResourceNodeId = (resource)=>resource.id;
const normalizeToolName = (resource)=>{
    if ('escalation' === resource.type) return `escalate_${resource.name.toLowerCase().replace(/\s/g, '_')}`;
    if ('mcp' === resource.type) return `mcp_${resource.name.toLowerCase().replace(/\s/g, '_')}`;
    if ('a2a' === resource.type) return `a2a_${resource.name.toLowerCase().replace(/\s/g, '_')}`;
    return resource.name.replace(/\s+/g, '_');
};
const hasResourceStatus = (resource, spans, targetStatus)=>{
    for (const span of spans){
        const attributes = span.Attributes ? JSON.parse(span.Attributes) : null;
        if (attributes) {
            if ('context' === resource.type || 'escalation' === resource.type || 'tool' === resource.type || 'mcp' === resource.type || 'a2a' === resource.type) {
                const normalizedToolName = normalizeToolName(resource);
                if (attributes.toolName === normalizedToolName && span.Status === targetStatus) return true;
            }
        }
    }
    return false;
};
const hasResourceRunning = (resource, spans)=>hasResourceStatus(resource, spans, SPAN_STATUS.RUNNING);
const hasResourceError = (resource, spans)=>hasResourceStatus(resource, spans, SPAN_STATUS.ERROR) || Boolean(resource.errors?.length);
const hasResourceSuccess = (resource, spans)=>{
    if (hasResourceRunning(resource, spans)) return false;
    return hasResourceStatus(resource, spans, SPAN_STATUS.SUCCESS);
};
const hasModelStatus = (_model, spans, targetStatus)=>{
    for (const span of spans){
        const attributes = span.Attributes ? JSON.parse(span.Attributes) : null;
        if (attributes) {
            if ('completion' === attributes.type && span.Status === targetStatus) return true;
        }
    }
    return false;
};
const hasModelError = (model, spans)=>hasModelStatus(model, spans, SPAN_STATUS.ERROR);
const hasModelRunning = (model, spans)=>hasModelStatus(model, spans, SPAN_STATUS.RUNNING);
const hasModelSuccess = (model, spans)=>{
    if (hasModelRunning(model, spans)) return false;
    return hasModelStatus(model, spans, SPAN_STATUS.SUCCESS);
};
const hasAgentRunning = (spans)=>{
    for (const span of spans){
        const attributes = span.Attributes ? JSON.parse(span.Attributes) : null;
        if (attributes) {
            if ('agentRun' === attributes.type) {
                const isRunning = span.Status === SPAN_STATUS.RUNNING;
                return isRunning;
            }
        }
    }
    return false;
};
const createAgentNode = (props, parentNodeId)=>{
    const agentId = parentNodeId ? `${parentNodeId}${NODE_ID_DELIMITER}${props.definition.processKey}` : props.definition.processKey || 'agent';
    return {
        id: agentId,
        type: 'agent',
        position: {
            x: 0,
            y: 0
        },
        data: {
            name: props.name,
            description: props.description,
            definition: props.definition,
            parentNodeId,
            instructions: props.instructions
        },
        draggable: Boolean(props.allowDragging && 'design' === props.mode)
    };
};
const createResourceNode = (resource, index, props, parentNodeId)=>{
    const id = getResourceNodeId(resource);
    const baseData = {
        name: resource.name,
        originalName: resource.originalName,
        description: resource.description,
        order: index,
        isActive: props.activeResourceIds?.includes(id) ?? false,
        hasError: hasResourceError(resource, props.spans),
        hasSuccess: hasResourceSuccess(resource, props.spans),
        hasRunning: hasResourceRunning(resource, props.spans),
        hasBreakpoint: resource.hasBreakpoint ?? false,
        isCurrentBreakpoint: resource.isCurrentBreakpoint ?? false,
        hasGuardrails: resource.hasGuardrails ?? false,
        isDisabled: resource.isDisabled ?? false,
        errors: resource.errors,
        errorAction: resource.errorAction
    };
    const hasExplicitPosition = 'position' in resource && 'object' == typeof resource.position && null !== resource.position;
    const position = hasExplicitPosition ? resource.position : {
        x: 0,
        y: 0
    };
    const resourceId = `${resource.name}:${id}`;
    const finalId = parentNodeId ? `${parentNodeId}${NODE_ID_DELIMITER}${resourceId}` : resourceId;
    const createBaseNode = (data)=>{
        const node = {
            id: finalId,
            type: 'resource',
            position,
            draggable: Boolean(props.allowDragging && 'design' === props.mode),
            data,
            hasExplicitPosition
        };
        return node;
    };
    switch(resource.type){
        case 'tool':
            return createBaseNode({
                ...baseData,
                type: 'tool',
                iconUrl: resource.iconUrl,
                projectType: resource.projectType,
                toolType: resource.toolType,
                projectId: resource.projectId,
                parentNodeId,
                isExpandable: resource.isExpandable,
                processName: resource.processName
            });
        case 'context':
            return createBaseNode({
                ...baseData,
                type: 'context',
                projectId: resource.projectId,
                parentNodeId
            });
        case 'mcp':
            return createBaseNode({
                ...baseData,
                type: 'mcp',
                slug: resource.slug,
                folderPath: resource.folderPath,
                availableTools: resource.availableTools,
                parentNodeId
            });
        case 'memorySpace':
            return createBaseNode({
                ...baseData,
                type: 'memorySpace',
                parentNodeId
            });
        case 'a2a':
            return createBaseNode({
                ...baseData,
                type: 'a2a',
                projectId: resource.projectId,
                parentNodeId
            });
        case 'escalation':
        default:
            return createBaseNode({
                ...baseData,
                type: 'escalation',
                projectId: resource.projectId,
                parentNodeId
            });
    }
};
const calculateOptimalHandles = (agentNode, resourceNode)=>{
    const resourceType = resourceNode.data.type;
    const agentHeight = agentNode.measured?.height ?? agentNode.height ?? 0;
    const agentCenterY = agentNode.position.y + agentHeight / 2;
    const resourceHeight = resourceNode.measured?.height ?? resourceNode.height ?? 0;
    const resourceCenterY = resourceNode.position.y + resourceHeight / 2;
    let sourceHandle;
    switch(resourceType){
        case 'context':
            sourceHandle = ResourceNodeType.Context;
            break;
        case 'escalation':
            sourceHandle = ResourceNodeType.Escalation;
            break;
        case 'tool':
            sourceHandle = ResourceNodeType.Tool;
            break;
        case 'mcp':
            sourceHandle = ResourceNodeType.Tool;
            break;
        case 'a2a':
            sourceHandle = ResourceNodeType.Tool;
            break;
        case 'memorySpace':
            sourceHandle = ResourceNodeType.MemorySpace;
            break;
        default:
            sourceHandle = resourceType;
            break;
    }
    const targetHandle = resourceCenterY < agentCenterY ? Position.Bottom : Position.Top;
    return {
        sourceHandle,
        targetHandle
    };
};
const createResourceEdge = (agentNode, resourceNode, props)=>{
    const isViewMode = 'view' === props.mode;
    const isResourceActive = resourceNode.data.isActive ?? false;
    const { sourceHandle, targetHandle } = calculateOptimalHandles(agentNode, resourceNode);
    const id = `${agentNode.id}${EDGE_ID_DELIMITER}${resourceNode.id}`;
    return {
        id,
        source: agentNode.id,
        target: resourceNode.id,
        sourceHandle,
        targetHandle,
        type: 'default',
        data: {
            label: null
        },
        animated: isViewMode && isResourceActive,
        selectable: false
    };
};
const computeNodesAndEdges = (props, parentNodeId, existingNodes)=>{
    const agentNode = createAgentNode(props, parentNodeId);
    const filteredResources = props.resources.filter((resource)=>{
        if ('mcp' === resource.type && false === props.enableMcpTools) return false;
        if ('a2a' === resource.type && true !== props.enableA2a) return false;
        return true;
    });
    const existingOrderMap = new Map();
    let maxExistingOrder = -1;
    if (existingNodes) for (const node of existingNodes){
        if (!isAgentFlowResourceNode(node)) continue;
        if (void 0 === node.data.order) continue;
        existingOrderMap.set(node.id, node.data.order);
        const parts = node.id.split(NODE_ID_DELIMITER);
        const baseId = parts[parts.length - 1] ?? node.id;
        const resourceIdPart = baseId.split(':')[1];
        if (resourceIdPart) existingOrderMap.set(resourceIdPart, node.data.order);
        maxExistingOrder = Math.max(maxExistingOrder, node.data.order);
    }
    let nextOrderIndex = maxExistingOrder + 1;
    const resourceNodes = filteredResources.map((resource)=>{
        const resourceId = getResourceNodeId(resource);
        let order = existingOrderMap.get(resourceId);
        if (void 0 === order) order = nextOrderIndex++;
        return createResourceNode(resource, order, props, agentNode.id);
    });
    const edges = resourceNodes.map((resourceNode)=>createResourceEdge(agentNode, resourceNode, props));
    const allNodes = [
        agentNode,
        ...resourceNodes
    ];
    const arrangedNodes = autoArrangeNodes(allNodes, edges, props.agentNodePosition, props.resourceNodePositions);
    return {
        nodes: arrangedNodes,
        edges
    };
};
const computeSuggestionNodesAndEdges = (suggestionGroup, props, agentNode, existingResources, existingNodes)=>{
    const suggestionNodes = [];
    const suggestionEdges = [];
    const existingOrderBySuggestionId = new Map();
    let maxExistingOrder = existingResources.length - 1;
    if (existingNodes) {
        for (const node of existingNodes)if (isAgentFlowResourceNode(node)) {
            if (void 0 !== node.data.order) {
                maxExistingOrder = Math.max(maxExistingOrder, node.data.order);
                if (node.data.suggestionId) existingOrderBySuggestionId.set(node.data.suggestionId, node.data.order);
            }
        }
    }
    let nextOrderIndex = maxExistingOrder + 1;
    for (const suggestion of suggestionGroup.suggestions)if ('add' === suggestion.type && suggestion.resource) {
        const resource = suggestion.resource;
        let order = existingOrderBySuggestionId.get(suggestion.id);
        if (void 0 === order) order = nextOrderIndex++;
        const node = createResourceNode(resource, order, props, agentNode.id);
        node.data = {
            ...node.data,
            isSuggestion: !suggestion.isStandalone,
            suggestionId: suggestion.id,
            suggestionType: 'add',
            isPlaceholder: true,
            hasRunning: suggestion.isProcessing
        };
        node.style = {
            ...node.style
        };
        suggestionNodes.push(node);
        suggestionEdges.push(createResourceEdge(agentNode, node, props));
    }
    return {
        nodes: suggestionNodes,
        edges: suggestionEdges
    };
};
export { EDGE_ID_DELIMITER, NODE_ID_DELIMITER, computeNodesAndEdges, computeSuggestionNodesAndEdges, createResourceEdge, hasAgentRunning, hasModelError, hasModelRunning, hasModelSuccess, hasResourceError, hasResourceRunning, hasResourceSuccess };
