import { useCallback, useRef } from 'react';
import type { DiagramSection } from './types';
import type { DiagramNode as DiagramNodeType } from '../../api/queries/diagrams';
import type { NodeExecutionState } from '../ExecutionDiagram/types';
import { colorForType } from './node-colors';
import styles from './ProcessDiagram.module.css';

const MINIMAP_WIDTH = 140;
const MINIMAP_HEIGHT = 90;
const MINIMAP_PADDING = 4;

interface MinimapProps {
  sections: DiagramSection[];
  totalWidth: number;
  totalHeight: number;
  /** Current zoom/pan state */
  scale: number;
  translateX: number;
  translateY: number;
  /** Container pixel dimensions */
  containerWidth: number;
  containerHeight: number;
  /** Called when user clicks/drags on minimap to pan */
  onPan: (translateX: number, translateY: number) => void;
  /** Execution overlay for coloring nodes */
  executionOverlay?: Map<string, NodeExecutionState>;
}

export function Minimap({
  sections, totalWidth, totalHeight,
  scale, translateX, translateY,
  containerWidth, containerHeight,
  onPan, executionOverlay,
}: MinimapProps) {
  const svgRef = useRef<SVGSVGElement>(null);
  const dragging = useRef(false);

  if (totalWidth <= 0 || totalHeight <= 0) return null;

  // Scale factor to fit diagram into minimap
  const innerW = MINIMAP_WIDTH - MINIMAP_PADDING * 2;
  const innerH = MINIMAP_HEIGHT - MINIMAP_PADDING * 2;
  const miniScale = Math.min(innerW / totalWidth, innerH / totalHeight);

  // Viewport rectangle in minimap coordinates
  // The visible area in diagram-space: from (-translateX/scale) to (-translateX/scale + containerWidth/scale)
  const vpX = (-translateX / scale) * miniScale + MINIMAP_PADDING;
  const vpY = (-translateY / scale) * miniScale + MINIMAP_PADDING;
  const vpW = (containerWidth / scale) * miniScale;
  const vpH = (containerHeight / scale) * miniScale;

  const handleClick = useCallback(
    (e: React.MouseEvent<SVGSVGElement>) => {
      const svg = svgRef.current;
      if (!svg) return;
      const rect = svg.getBoundingClientRect();
      const mx = e.clientX - rect.left - MINIMAP_PADDING;
      const my = e.clientY - rect.top - MINIMAP_PADDING;
      // Convert minimap click to diagram coordinates, then to translate
      const diagramX = mx / miniScale;
      const diagramY = my / miniScale;
      // Center the viewport on the clicked point
      const newTx = -(diagramX * scale) + containerWidth / 2;
      const newTy = -(diagramY * scale) + containerHeight / 2;
      onPan(newTx, newTy);
    },
    [miniScale, scale, containerWidth, containerHeight, onPan],
  );

  const handlePointerDown = useCallback(
    (e: React.PointerEvent<SVGSVGElement>) => {
      dragging.current = true;
      (e.currentTarget as SVGSVGElement).setPointerCapture(e.pointerId);
    },
    [],
  );

  const handlePointerMove = useCallback(
    (e: React.PointerEvent<SVGSVGElement>) => {
      if (!dragging.current) return;
      const svg = svgRef.current;
      if (!svg) return;
      const rect = svg.getBoundingClientRect();
      const mx = e.clientX - rect.left - MINIMAP_PADDING;
      const my = e.clientY - rect.top - MINIMAP_PADDING;
      const diagramX = mx / miniScale;
      const diagramY = my / miniScale;
      const newTx = -(diagramX * scale) + containerWidth / 2;
      const newTy = -(diagramY * scale) + containerHeight / 2;
      onPan(newTx, newTy);
    },
    [miniScale, scale, containerWidth, containerHeight, onPan],
  );

  const handlePointerUp = useCallback(
    (e: React.PointerEvent<SVGSVGElement>) => {
      dragging.current = false;
      (e.currentTarget as SVGSVGElement).releasePointerCapture(e.pointerId);
    },
    [],
  );

  return (
    <div className={styles.minimap}>
      <svg
        ref={svgRef}
        width={MINIMAP_WIDTH}
        height={MINIMAP_HEIGHT}
        onClick={handleClick}
        onPointerDown={handlePointerDown}
        onPointerMove={handlePointerMove}
        onPointerUp={handlePointerUp}
        style={{ cursor: 'crosshair' }}
      >
        {/* Background */}
        <rect width={MINIMAP_WIDTH} height={MINIMAP_HEIGHT} fill="var(--bg-surface, #FFFFFF)" rx={3} />

        <g transform={`translate(${MINIMAP_PADDING}, ${MINIMAP_PADDING}) scale(${miniScale})`}>
          {/* Render simplified nodes for each section */}
          {sections.map((section, si) => (
            <g key={si} transform={`translate(0, ${section.offsetY})`}>
              {renderMinimapNodes(section.nodes, executionOverlay)}
            </g>
          ))}
        </g>

        {/* Viewport indicator */}
        <rect
          x={vpX}
          y={vpY}
          width={Math.max(vpW, 4)}
          height={Math.max(vpH, 4)}
          fill="var(--amber, #C6820E)"
          fillOpacity={0.15}
          stroke="var(--amber, #C6820E)"
          strokeWidth={1.5}
          rx={1}
        />
      </svg>
    </div>
  );
}

function nodeColor(
  node: DiagramNodeType,
  overlay?: Map<string, NodeExecutionState>,
): { fill: string; opacity: number } {
  if (!overlay) return { fill: colorForType(node.type), opacity: 0.7 };
  const state = node.id ? overlay.get(node.id) : undefined;
  if (state?.status === 'COMPLETED') return { fill: 'var(--success)', opacity: 0.85 };
  if (state?.status === 'FAILED') return { fill: 'var(--error)', opacity: 0.85 };
  // ENDPOINT is always traversed when overlay is active (route entry point)
  if (node.type === 'ENDPOINT' && overlay.size > 0) return { fill: 'var(--success)', opacity: 0.85 };
  // Skipped (overlay active but node not executed)
  return { fill: 'var(--text-muted)', opacity: 0.35 };
}

function renderMinimapNodes(
  nodes: DiagramNodeType[],
  overlay?: Map<string, NodeExecutionState>,
): React.ReactNode[] {
  const elements: React.ReactNode[] = [];
  for (const node of nodes) {
    const x = node.x ?? 0;
    const y = node.y ?? 0;
    const w = node.width ?? 160;
    const h = node.height ?? 40;

    if (node.children && node.children.length > 0) {
      const { fill, opacity } = nodeColor(node, overlay);
      elements.push(
        <rect key={node.id} x={x} y={y} width={w} height={h}
          fill="none" stroke={fill} strokeWidth={2} rx={2} opacity={opacity} />,
      );
      elements.push(...renderMinimapNodes(node.children, overlay));
    } else {
      const { fill, opacity } = nodeColor(node, overlay);
      elements.push(
        <rect key={node.id} x={x} y={y} width={w} height={h}
          fill={fill} rx={2} opacity={opacity} />,
      );
    }
  }
  return elements;
}
