import type { DiagramNode as DiagramNodeType, DiagramEdge as DiagramEdgeType } from '../../api/queries/diagrams';
import type { NodeConfig } from './types';
import type { NodeExecutionState } from '../ExecutionDiagram/types';
import { colorForType, isCompoundType } from './node-colors';
import { DiagramNode } from './DiagramNode';
import { DiagramEdge } from './DiagramEdge';

const HEADER_HEIGHT = 22;
const CORNER_RADIUS = 4;

interface CompoundNodeProps {
  node: DiagramNodeType;
  /** All edges for this section — compound filters to its own internal edges */
  edges: DiagramEdgeType[];
  /** Absolute offset of the nearest compound ancestor (for coordinate adjustment) */
  parentX?: number;
  parentY?: number;
  selectedNodeId?: string;
  hoveredNodeId: string | null;
  nodeConfigs?: Map<string, NodeConfig>;
  /** Execution overlay for edge traversal coloring */
  executionOverlay?: Map<string, NodeExecutionState>;
  onNodeClick: (nodeId: string) => void;
  onNodeDoubleClick?: (nodeId: string) => void;
  onNodeEnter: (nodeId: string) => void;
  onNodeLeave: () => void;
}

export function CompoundNode({
  node, edges, parentX = 0, parentY = 0,
  selectedNodeId, hoveredNodeId, nodeConfigs, executionOverlay,
  onNodeClick, onNodeDoubleClick, onNodeEnter, onNodeLeave,
}: CompoundNodeProps) {
  const x = (node.x ?? 0) - parentX;
  const y = (node.y ?? 0) - parentY;
  const absX = node.x ?? 0;
  const absY = node.y ?? 0;
  const w = node.width ?? 200;
  const h = node.height ?? 100;
  const color = colorForType(node.type);
  const typeName = node.type?.replace(/^EIP_/, '').replace(/_/g, ' ') ?? '';
  const label = node.label ? `${typeName}: ${node.label}` : typeName;

  // Collect all descendant node IDs to filter edges that belong inside this compound
  const descendantIds = new Set<string>();
  collectIds(node.children ?? [], descendantIds);

  const internalEdges = edges.filter(
    e => descendantIds.has(e.sourceId) && descendantIds.has(e.targetId),
  );

  return (
    <g data-node-id={node.id} transform={`translate(${x}, ${y})`}>
      {/* Container body */}
      <rect
        x={0}
        y={0}
        width={w}
        height={h}
        rx={CORNER_RADIUS}
        fill="white"
        stroke={color}
        strokeWidth={1.5}
      />

      {/* Colored header bar */}
      <rect x={0} y={0} width={w} height={HEADER_HEIGHT} rx={CORNER_RADIUS} fill={color} />
      <rect x={CORNER_RADIUS} y={CORNER_RADIUS} width={w - CORNER_RADIUS * 2} height={HEADER_HEIGHT - CORNER_RADIUS} fill={color} />

      {/* Header label */}
      <text
        x={w / 2}
        y={HEADER_HEIGHT / 2 + 4}
        fill="white"
        fontSize={10}
        fontWeight={600}
        textAnchor="middle"
      >
        {label}
      </text>

      {/* Internal edges (rendered after background, before children) */}
      <g className="edges">
        {internalEdges.map((edge, i) => {
          const isTraversed = executionOverlay
            ? (executionOverlay.has(edge.sourceId) && executionOverlay.has(edge.targetId))
            : undefined;
          return (
            <DiagramEdge
              key={`${edge.sourceId}-${edge.targetId}-${i}`}
              edge={{
                ...edge,
                points: edge.points.map(p => [p[0] - absX, p[1] - absY]),
              }}
              traversed={isTraversed}
            />
          );
        })}
      </g>

      {/* Children — recurse into compound children, render leaves as DiagramNode */}
      {node.children?.map(child => {
        if (isCompoundType(child.type) && child.children && child.children.length > 0) {
          return (
            <CompoundNode
              key={child.id}
              node={child}
              edges={edges}
              parentX={absX}
              parentY={absY}
              selectedNodeId={selectedNodeId}
              hoveredNodeId={hoveredNodeId}
              nodeConfigs={nodeConfigs}
              executionOverlay={executionOverlay}
              onNodeClick={onNodeClick}
              onNodeDoubleClick={onNodeDoubleClick}
              onNodeEnter={onNodeEnter}
              onNodeLeave={onNodeLeave}
            />
          );
        }
        return (
          <DiagramNode
            key={child.id}
            node={{
              ...child,
              x: (child.x ?? 0) - absX,
              y: (child.y ?? 0) - absY,
            }}
            isHovered={hoveredNodeId === child.id}
            isSelected={selectedNodeId === child.id}
            config={child.id ? nodeConfigs?.get(child.id) : undefined}
            onClick={() => child.id && onNodeClick(child.id)}
            onDoubleClick={() => child.id && onNodeDoubleClick?.(child.id)}
            onMouseEnter={() => child.id && onNodeEnter(child.id)}
            onMouseLeave={onNodeLeave}
          />
        );
      })}
    </g>
  );
}

function collectIds(nodes: DiagramNodeType[], set: Set<string>) {
  for (const n of nodes) {
    if (n.id) set.add(n.id);
    if (n.children) collectIds(n.children, set);
  }
}
