import { useEffect, useRef, useState, useCallback } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { useAppStore } from '../../store/appStore';
import type { FlowNode } from '../../types/scripts';
import styles from './FlowViewer.module.css';

// ── Layout constants ──────────────────────────────────────────────────────────

const NODE_W = 220;
const NODE_H = 44;
const H_GAP = 30;   // horizontal gap between sibling subtrees
const V_GAP = 60;   // vertical gap between levels

// ── Layout engine ─────────────────────────────────────────────────────────────

interface LayoutNode {
  node: FlowNode;
  x: number;
  y: number;
  width: number;  // subtree width
}

function layoutTree(
  node: FlowNode,
  depth: number,
  xOffset: number
): { positioned: LayoutNode[]; subtreeWidth: number } {
  if (node.children.length === 0) {
    return {
      positioned: [{ node, x: xOffset, y: depth * (NODE_H + V_GAP), width: NODE_W }],
      subtreeWidth: NODE_W,
    };
  }

  let childX = xOffset;
  const allPositioned: LayoutNode[] = [];
  let totalWidth = 0;

  for (let i = 0; i < node.children.length; i++) {
    const child = node.children[i];
    const result = layoutTree(child, depth + 1, childX);
    allPositioned.push(...result.positioned);
    childX += result.subtreeWidth + (i < node.children.length - 1 ? H_GAP : 0);
    totalWidth += result.subtreeWidth + (i < node.children.length - 1 ? H_GAP : 0);
  }

  // Center this node over its children
  const subtreeWidth = Math.max(totalWidth, NODE_W);
  const nodeX = xOffset + (subtreeWidth - NODE_W) / 2;

  allPositioned.push({
    node,
    x: nodeX,
    y: depth * (NODE_H + V_GAP),
    width: subtreeWidth,
  });

  return { positioned: allPositioned, subtreeWidth };
}

function buildEdges(positioned: LayoutNode[]): Array<{ x1: number; y1: number; x2: number; y2: number }> {
  const posMap = new Map<string, LayoutNode>();
  for (const p of positioned) posMap.set(p.node.id, p);

  const edges: Array<{ x1: number; y1: number; x2: number; y2: number }> = [];

  function walk(node: FlowNode) {
    const parent = posMap.get(node.id);
    if (!parent) return;
    for (const child of node.children) {
      const c = posMap.get(child.id);
      if (c) {
        edges.push({
          x1: parent.x + NODE_W / 2,
          y1: parent.y + NODE_H,
          x2: c.x + NODE_W / 2,
          y2: c.y,
        });
      }
      walk(child);
    }
  }
  for (const p of positioned) walk(p.node);
  return edges;
}

// ── Node colors by type ───────────────────────────────────────────────────────

function nodeStyle(type: FlowNode['type']): { fill: string; stroke: string; text: string } {
  switch (type) {
    case 'gump_send':
      return { fill: '#1a1a2e', stroke: '#8888ff', text: '#aaaaff' };
    case 'condition':
      return { fill: '#1e1a10', stroke: '#c8a84b', text: '#e8c86b' };
    case 'branch_true':
      return { fill: '#0d1a0d', stroke: '#3a6b3a', text: '#88cc88' };
    case 'branch_false':
      return { fill: '#1a0d0d', stroke: '#6b3a3a', text: '#cc8888' };
    case 'branch_case':
      return { fill: '#0d1520', stroke: '#3a5a7a', text: '#8aaabb' };
    case 'return':
      return { fill: '#1a1010', stroke: '#8b3a3a', text: '#cc6666' };
    case 'property_access':
      return { fill: '#141418', stroke: '#4a4060', text: '#8a7ab0' };
    default:
      return { fill: '#161410', stroke: '#3a3020', text: '#d4c49a' };
  }
}

// ── Component ─────────────────────────────────────────────────────────────────

export default function FlowViewer() {
  const { selectedMethod, selectedClass, flowRoot, setFlowRoot, fakeData } = useAppStore();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const svgRef = useRef<SVGSVGElement>(null);
  const [scale, setScale] = useState(1);
  const [pan, setPan] = useState({ x: 20, y: 20 });
  const isPanning = useRef(false);
  const panStart = useRef({ x: 0, y: 0 });

  const traceMethod = useCallback(async () => {
    if (!selectedClass || !selectedMethod) return;
    setLoading(true);
    setError(null);
    try {
      const node = await invoke<FlowNode>('trace_method', {
        className: selectedClass.name,
        methodName: selectedMethod.name,
      });
      setFlowRoot(node);
    } catch (e) {
      setError(String(e));
      setFlowRoot(null);
    } finally {
      setLoading(false);
    }
  }, [selectedClass?.name, selectedMethod?.name]);

  // Auto-trace when selection changes
  useEffect(() => {
    if (selectedClass && selectedMethod) {
      traceMethod();
    }
  }, [selectedClass?.name, selectedMethod?.name]);

  // SVG pan & zoom
  function onWheel(e: React.WheelEvent) {
    e.preventDefault();
    setScale((s) => Math.min(2, Math.max(0.3, s - e.deltaY * 0.001)));
  }

  function onMouseDown(e: React.MouseEvent) {
    isPanning.current = true;
    panStart.current = { x: e.clientX - pan.x, y: e.clientY - pan.y };
  }

  function onMouseMove(e: React.MouseEvent) {
    if (!isPanning.current) return;
    setPan({ x: e.clientX - panStart.current.x, y: e.clientY - panStart.current.y });
  }

  function onMouseUp() {
    isPanning.current = false;
  }

  if (!selectedClass || !selectedMethod) {
    return (
      <div className={styles.empty}>
        Select a method from the left panel to trace its call chain.
      </div>
    );
  }

  if (loading) {
    return <div className={styles.empty}>Tracing {selectedMethod.name}…</div>;
  }

  if (error) {
    return (
      <div className={styles.errorWrapper}>
        <div className={styles.errorTitle}>Trace failed</div>
        <div className={styles.errorMsg}>{error}</div>
        <button className={styles.retryBtn} onClick={traceMethod}>Retry</button>
      </div>
    );
  }

  if (!flowRoot) return null;

  const { positioned } = layoutTree(flowRoot, 0, 0);
  const edges = buildEdges(positioned);

  const maxX = Math.max(...positioned.map((p) => p.x + NODE_W)) + 40;
  const maxY = Math.max(...positioned.map((p) => p.y + NODE_H)) + 40;

  return (
    <div className={styles.wrapper}>
      {/* Toolbar */}
      <div className={styles.toolbar}>
        <span className={`${styles.methodLabel} font-cinzel`}>
          {selectedClass.name}.{selectedMethod.name}
        </span>
        <button className={styles.toolBtn} onClick={traceMethod}>↺ Retrace</button>
        <button className={styles.toolBtn} onClick={() => { setScale(1); setPan({ x: 20, y: 20 }); }}>
          Reset View
        </button>
      </div>

      {/* SVG canvas */}
      <div
        className={styles.canvas}
        onWheel={onWheel}
        onMouseDown={onMouseDown}
        onMouseMove={onMouseMove}
        onMouseUp={onMouseUp}
        onMouseLeave={onMouseUp}
      >
        <svg
          ref={svgRef}
          width={maxX}
          height={maxY}
          style={{ transform: `translate(${pan.x}px, ${pan.y}px) scale(${scale})`, transformOrigin: '0 0' }}
        >
          {/* Edges */}
          {edges.map((e, i) => (
            <path
              key={i}
              d={`M${e.x1},${e.y1} C${e.x1},${(e.y1 + e.y2) / 2} ${e.x2},${(e.y1 + e.y2) / 2} ${e.x2},${e.y2}`}
              stroke="var(--border-accent)"
              strokeWidth={1.5}
              fill="none"
              opacity={0.6}
            />
          ))}

          {/* Nodes */}
          {positioned.map(({ node, x, y }) => {
            const s = nodeStyle(node.type);
            const isFakeActive =
              node.fakeInputKey && fakeData[node.fakeInputKey] !== undefined;

            return (
              <g key={node.id} transform={`translate(${x},${y})`}>
                <rect
                  width={NODE_W}
                  height={NODE_H}
                  rx={4}
                  fill={s.fill}
                  stroke={isFakeActive ? '#e8c86b' : s.stroke}
                  strokeWidth={isFakeActive ? 2 : 1}
                />
                <foreignObject x={4} y={4} width={NODE_W - 8} height={NODE_H - 8}>
                  <div
                    style={{
                      color: s.text,
                      fontSize: '11px',
                      fontFamily: "'JetBrains Mono', monospace",
                      lineHeight: 1.3,
                      overflow: 'hidden',
                      textOverflow: 'ellipsis',
                      whiteSpace: 'nowrap',
                      padding: '2px 4px',
                      userSelect: 'none',
                    }}
                    title={node.label}
                  >
                    <span style={{ opacity: 0.6, fontSize: '9px' }}>{node.type} </span>
                    {node.label}
                    {node.resolvedGump && (
                      <div style={{ fontSize: '9px', opacity: 0.7, marginTop: 2 }}>
                        → {node.resolvedGump}
                      </div>
                    )}
                  </div>
                </foreignObject>
              </g>
            );
          })}
        </svg>
      </div>
    </div>
  );
}
