import type { RouteNode, FlowSegment } from '@cameleer/design-system';
import type { DiagramNode } from '../api/queries/diagrams';

// Map NodeType strings to RouteNode types
function mapNodeType(type: string): RouteNode['type'] {
  const lower = type?.toLowerCase() || '';
  if (lower.includes('from') || lower === 'endpoint') return 'from';
  if (lower.includes('exception') || lower.includes('error_handler')
      || lower.includes('dead_letter')) return 'error-handler';
  if (lower === 'to' || lower === 'to_dynamic' || lower === 'direct'
      || lower === 'seda') return 'to';
  if (lower.includes('choice') || lower.includes('when')
      || lower.includes('otherwise')) return 'choice';
  return 'process';
}

function mapStatus(status: string | undefined): RouteNode['status'] {
  if (!status) return 'ok';
  const s = status.toUpperCase();
  if (s === 'FAILED') return 'fail';
  if (s === 'RUNNING') return 'slow';
  return 'ok';
}

type ProcessorNode = { processorId?: string; status?: string; durationMs?: number; children?: ProcessorNode[] };

function buildProcMap(processors: ProcessorNode[]): Map<string, ProcessorNode> {
  const map = new Map<string, ProcessorNode>();
  function walk(nodes: ProcessorNode[]) {
    for (const n of nodes) {
      if (n.processorId) map.set(n.processorId, n);
      if (n.children) walk(n.children);
    }
  }
  walk(processors || []);
  return map;
}

function toRouteNode(node: DiagramNode, procMap: Map<string, ProcessorNode>): RouteNode {
  const proc = procMap.get(node.id ?? '');
  return {
    name: node.label || node.id || '',
    type: mapNodeType(node.type ?? ''),
    durationMs: proc?.durationMs ?? 0,
    status: mapStatus(proc?.status),
    isBottleneck: false,
  };
}

/**
 * Builds FlowSegment[] from diagram nodes, properly separating error handler
 * compounds (ON_EXCEPTION, ERROR_HANDLER) into distinct flow sections.
 *
 * Returns:
 * - flows: FlowSegment[] with main route + error handler sections
 * - nodeIds: flat array of diagram node IDs aligned across all flows
 *   (main flow IDs first, then error handler children IDs)
 */
export function buildFlowSegments(
  diagramNodes: DiagramNode[],
  processors: ProcessorNode[],
): { flows: FlowSegment[]; nodeIds: string[] } {
  const procMap = buildProcMap(processors);

  const mainNodes: RouteNode[] = [];
  const mainIds: string[] = [];
  const errorFlows: { label: string; nodes: RouteNode[]; ids: string[] }[] = [];

  for (const node of diagramNodes) {
    const type = mapNodeType(node.type ?? '');

    if (type === 'error-handler' && node.children && node.children.length > 0) {
      // Error handler compound → separate flow segment with its children
      const children: RouteNode[] = [];
      const childIds: string[] = [];
      for (const child of node.children) {
        children.push(toRouteNode(child, procMap));
        childIds.push(child.id ?? '');
      }
      errorFlows.push({
        label: node.label || 'Error Handler',
        nodes: children,
        ids: childIds,
      });
    } else {
      // Regular node → main flow
      mainNodes.push(toRouteNode(node, procMap));
      mainIds.push(node.id ?? '');
    }
  }

  const flows: FlowSegment[] = [{ label: 'Main Route', nodes: mainNodes }];
  const nodeIds = [...mainIds];

  for (const ef of errorFlows) {
    flows.push({ label: ef.label, nodes: ef.nodes, variant: 'error' });
    nodeIds.push(...ef.ids);
  }

  return { flows, nodeIds };
}

/**
 * Legacy: splits a flat RouteNode[] into FlowSegment[] by type.
 * Used as fallback when no diagram data is available.
 */
export function toFlowSegments(nodes: RouteNode[]): { flows: FlowSegment[] } {
  const mainNodes = nodes.filter(n => n.type !== 'error-handler');
  const errorNodes = nodes.filter(n => n.type === 'error-handler');

  const flows: FlowSegment[] = [
    { label: 'Main Route', nodes: mainNodes },
  ];
  if (errorNodes.length > 0) {
    flows.push({ label: 'Error Handler', nodes: errorNodes, variant: 'error' });
  }

  return { flows };
}
