import { useEffect, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import {
  ReactFlow,
  ReactFlowProvider,
  Background,
  Controls,
  MiniMap,
  type Node,
  type Edge,
  type NodeProps,
  Handle,
  Position,
  useNodesState,
  useEdgesState,
  useReactFlow,
  BackgroundVariant,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import dagre from 'dagre';
import { type LineageNode } from '../../api/client';

interface Props {
  tree: LineageNode;
  ancestorChain?: { name: string; status: string }[];
  current?: string;
  height?: number;
}

const STATUS_COLOR: Record<string, string> = {
  promoted: '#059669',
  active:   '#0077b6',
  draft:    '#b8860b',
  retired:  '#9ca3af',
  archived: '#d1d5db',
};

const NODE_W = 220;
const NODE_H = 60;

// ── Build flat nodes/edges from tree + ancestor chain ──────────────────────
function buildGraph(
  tree: LineageNode,
  ancestorChain: { name: string; status: string }[],
): { nodes: Node[]; edges: Edge[] } {
  const nodes: Node[] = [];
  const edges: Edge[] = [];
  const seen = new Set<string>();

  function addNode(
    name: string,
    status: string,
    sqs: number | null,
    aliases: string[],
    isAncestorOnly = false,
  ) {
    if (seen.has(name)) return;
    seen.add(name);
    nodes.push({
      id: name,
      type: 'strategyNode',
      position: { x: 0, y: 0 },
      data: { name, status, sqs, aliases, isAncestorOnly },
    });
  }

  function addEdge(parent: string, child: string) {
    edges.push({
      id: `${parent}→${child}`,
      source: parent,
      target: child,
      type: 'smoothstep',
      style: { stroke: '#d1d5db', strokeWidth: 1.5 },
    });
  }

  const ancestors = ancestorChain.slice(0, -1);
  for (let i = 0; i < ancestors.length; i++) {
    addNode(ancestors[i].name, ancestors[i].status, null, [], true);
    if (i > 0) addEdge(ancestors[i - 1].name, ancestors[i].name);
  }
  if (ancestors.length > 0) {
    addEdge(ancestors[ancestors.length - 1].name, tree.name);
  }

  const queue: LineageNode[] = [tree];
  while (queue.length > 0) {
    const node = queue.shift()!;
    addNode(node.name, node.meta?.status ?? 'active', node.meta?.sqs_score ?? null, node.meta?.aliases ?? []);
    for (const child of node.children ?? []) {
      addEdge(node.name, child.name);
      queue.push(child);
    }
  }

  return { nodes, edges };
}

// ── Dagre auto-layout ──────────────────────────────────────────────────────
function applyLayout(nodes: Node[], edges: Edge[]): Node[] {
  const g = new dagre.graphlib.Graph();
  g.setDefaultEdgeLabel(() => ({}));
  g.setGraph({ rankdir: 'TB', ranksep: 70, nodesep: 24, marginx: 40, marginy: 40 });
  nodes.forEach(n => g.setNode(n.id, { width: NODE_W, height: NODE_H }));
  edges.forEach(e => g.setEdge(e.source, e.target));
  dagre.layout(g);
  return nodes.map(n => {
    const pos = g.node(n.id);
    return { ...n, position: { x: pos.x - NODE_W / 2, y: pos.y - NODE_H / 2 } };
  });
}

// ── Node: regular strategy ─────────────────────────────────────────────────
function StrategyNode({ data }: NodeProps) {
  const navigate = useNavigate();
  const { name, status, sqs, aliases, isAncestorOnly } = data as {
    name: string; status: string; sqs: number | null; aliases: string[]; isAncestorOnly: boolean;
  };
  const statusColor = STATUS_COLOR[status] ?? '#9ca3af';
  const displayStatus = status === 'promoted' ? 'pinned' : status;

  return (
    <>
      <Handle type="target" position={Position.Top} style={{ opacity: 0 }} />
      <div
        onClick={() => navigate(`/experiments/${encodeURIComponent(name)}`)}
        style={{
          width: NODE_W, height: NODE_H,
          background: isAncestorOnly ? '#f9fafb' : 'white',
          border: `1.5px solid ${isAncestorOnly ? '#e5e7eb' : '#d1d5db'}`,
          borderRadius: 10, padding: '10px 14px', cursor: 'pointer',
          boxShadow: isAncestorOnly ? 'none' : '0 1px 4px rgba(0,0,0,0.07)',
          display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 4,
        }}
      >
        <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <div style={{ width: 7, height: 7, borderRadius: '50%', background: statusColor, flexShrink: 0 }} />
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 600, color: '#1a1d24',
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
            maxWidth: NODE_W - 50,
          }}>{name}</span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, paddingLeft: 14 }}>
          {sqs != null ? (
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: '#b8860b', fontWeight: 600 }}>
              SQS {sqs.toFixed(1)}
            </span>
          ) : (
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: '#9ca3af' }}>{displayStatus}</span>
          )}
          {aliases.map(a => (
            <span key={a} style={{
              fontFamily: 'var(--font-mono)', fontSize: 10,
              background: '#ede9fe', color: '#7c3aed',
              border: '1px solid rgba(124,58,237,0.18)',
              padding: '1px 5px', borderRadius: 4,
            }}>{a}</span>
          ))}
        </div>
      </div>
      <Handle type="source" position={Position.Bottom} style={{ opacity: 0 }} />
    </>
  );
}

// ── Node: current (highlighted) ────────────────────────────────────────────
function CurrentStrategyNode({ data }: NodeProps) {
  const navigate = useNavigate();
  const { name, sqs, aliases } = data as {
    name: string; status: string; sqs: number | null; aliases: string[]; isAncestorOnly: boolean;
  };

  return (
    <>
      <Handle type="target" position={Position.Top} style={{ opacity: 0 }} />
      <div
        onClick={() => navigate(`/experiments/${encodeURIComponent(name)}`)}
        style={{
          width: NODE_W, height: NODE_H,
          background: 'rgba(0,119,182,0.06)',
          border: '2px solid #0077b6',
          borderRadius: 10, padding: '10px 14px', cursor: 'pointer',
          boxShadow: '0 0 0 3px rgba(0,119,182,0.12)',
          display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 4,
        }}
      >
        <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <div style={{ width: 7, height: 7, borderRadius: '50%', background: '#0077b6', flexShrink: 0 }} />
          <span style={{
            fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 700, color: '#0077b6',
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
            maxWidth: NODE_W - 50,
          }}>{name}</span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, paddingLeft: 14 }}>
          {sqs != null ? (
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: '#b8860b', fontWeight: 600 }}>
              SQS {sqs.toFixed(1)}
            </span>
          ) : (
            <span style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: '#0077b6' }}>current</span>
          )}
          {aliases.map(a => (
            <span key={a} style={{
              fontFamily: 'var(--font-mono)', fontSize: 10,
              background: '#ede9fe', color: '#7c3aed',
              border: '1px solid rgba(124,58,237,0.18)',
              padding: '1px 5px', borderRadius: 4,
            }}>{a}</span>
          ))}
        </div>
      </div>
      <Handle type="source" position={Position.Bottom} style={{ opacity: 0 }} />
    </>
  );
}

const nodeTypes = {
  strategyNode: StrategyNode,
  currentNode: CurrentStrategyNode,
};

// ── Inner flow component (uses useReactFlow) ───────────────────────────────
interface FlowProps {
  layoutedNodes: Node[];
  edges: Edge[];
  focusIds: { id: string }[];
  current?: string;
}

function FlowInner({ layoutedNodes, edges, focusIds: _focusIds, current }: FlowProps) {
  const [nodes, , onNodesChange] = useNodesState(layoutedNodes);
  const [edgesState, , onEdgesChange] = useEdgesState(edges);
  const { setViewport } = useReactFlow();

  // Center on current node after mount
  useEffect(() => {
    const currentNode = layoutedNodes.find(n => n.id === current);
    if (!currentNode) return;

    const zoom = 0.85;
    const nodeCenterX = currentNode.position.x + NODE_W / 2;
    const nodeCenterY = currentNode.position.y + NODE_H / 2;
    const containerW = window.innerWidth - 240; // viewport minus sidebar

    setViewport({
      x: containerW / 2 - nodeCenterX * zoom,
      y: 180 - nodeCenterY * zoom, // 180px from top → shows ~2 ancestor levels above
      zoom,
    });
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // once on mount

  return (
    <ReactFlow
      nodes={nodes}
      edges={edgesState}
      onNodesChange={onNodesChange}
      onEdgesChange={onEdgesChange}
      nodeTypes={nodeTypes}
      minZoom={0.05}
      maxZoom={3}
      proOptions={{ hideAttribution: true }}
    >
      <Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#e5e7eb" />
      <Controls position="bottom-right" showInteractive={false} />
      <MiniMap
        nodeColor={n => {
          if (n.id === current) return '#0077b6';
          const status = (n.data as any)?.status;
          return STATUS_COLOR[status] ?? '#9ca3af';
        }}
        position="bottom-left"
        style={{ borderRadius: 8, border: '1px solid var(--border)' }}
      />
    </ReactFlow>
  );
}

// ── Export ─────────────────────────────────────────────────────────────────
export function LineageTree({ tree, ancestorChain, current, height = 640 }: Props) {
  const chain = ancestorChain ?? [];

  const { rawNodes, edges } = useMemo(() => {
    const { nodes, edges } = buildGraph(tree, chain);
    const markedNodes = nodes.map(n =>
      n.id === current ? { ...n, type: 'currentNode' } : n,
    );
    return { rawNodes: markedNodes, edges };
  }, [tree, chain, current]);

  const layoutedNodes = useMemo(() => applyLayout(rawNodes, edges), [rawNodes, edges]);

  const focusIds = useMemo(() => {
    const ids = new Set<string>();
    chain.forEach(a => ids.add(a.name));
    ids.add(tree.name);
    tree.children?.forEach(c => ids.add(c.name));
    return [...ids].map(id => ({ id }));
  }, [chain, tree]);

  return (
    <div style={{
      background: 'var(--bg1)', border: '1px solid var(--border)',
      borderRadius: 12, overflow: 'hidden', height,
      boxShadow: '0 1px 3px rgba(0,0,0,0.04)',
    }}>
      <ReactFlowProvider>
        <FlowInner
          layoutedNodes={layoutedNodes}
          edges={edges}
          focusIds={focusIds}
          current={current}
        />
      </ReactFlowProvider>
    </div>
  );
}
