"use client";

import { useCallback, useMemo, useState } from "react";
import {
  ReactFlow,
  ReactFlowProvider,
  Background,
  BackgroundVariant,
  Controls,
  Handle,
  Position,
  useReactFlow,
  type Edge,
  type Node,
  type NodeProps,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import dagre from "@dagrejs/dagre";
import { toPng } from "html-to-image";
import {
  Boxes,
  Database,
  AppWindow,
  MapPin,
  Truck,
  Server,
  Package,
  FolderKanban,
  Users,
  FileText,
  GitBranch,
  Crosshair,
  Search,
  Download,
  Zap,
  ZapOff,
} from "lucide-react";
import { cn } from "@/lib/utils";
import type { DependencyGraph, GraphNode, GraphNodeKind } from "@/server/dependency-graph";

const KIND_ICON: Record<GraphNodeKind, typeof Boxes> = {
  process: GitBranch,
  INFORMATION: FileText,
  SYSTEM: Boxes,
  APPLICATION: AppWindow,
  LOCATION: MapPin,
  SUPPLIER: Truck,
  IT_SERVICE: Server,
  SOFTWARE: Package,
  PROJECT: FolderKanban,
  PERSON: Users,
  DATA: Database,
};

type NodeData = GraphNode & { dimmed: boolean; kindLabel: string };

/** Custom-Node im Topology-Look (Referenz ISMS-Abhaengigkeitskarte). */
function IsmsNode({ data }: NodeProps<Node<NodeData>>) {
  const Icon = KIND_ICON[data.kind] ?? Boxes;
  const critColor =
    data.criticality >= 4 ? "#ff6b6b" : data.criticality >= 3 ? "#f0ad4e" : "#39c07f";
  return (
    <div
      className={cn(
        "flex w-49 items-center gap-2.5 rounded-2xl border px-3 py-2.5 transition-all",
        "bg-[var(--panel)] backdrop-blur",
        data.critical
          ? "border-[rgba(255,107,107,0.5)] shadow-[0_0_0_1px_rgba(255,107,107,0.25),0_8px_26px_rgba(255,80,80,0.12)]"
          : "border-[var(--panel-brd)]",
        data.dimmed && "opacity-25"
      )}
    >
      <Handle type="target" position={Position.Left} className="!bg-[var(--muted-foreground)]" />
      <span
        className={cn(
          "grid size-9 shrink-0 place-items-center rounded-xl",
          data.critical ? "bg-[rgba(255,107,107,0.16)] text-[#ff6b6b]" : "bg-[rgba(125,111,214,0.16)] text-[#8dc4e0]"
        )}
      >
        <Icon className="size-4.5" />
      </span>
      <div className="min-w-0 flex-1">
        <div className="flex items-center gap-1.5">
          <span className="truncate text-[13px] font-semibold text-[var(--txt,#e8ecf7)]">
            {data.name}
          </span>
        </div>
        <div className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
          <span className="inline-block size-2 rounded-full" style={{ background: critColor }} />
          {data.kindLabel} · K{data.criticality}
          {data.spof && (
            <span className="ml-0.5 rounded bg-[rgba(255,107,107,0.18)] px-1.5 py-px text-[9.5px] font-bold text-[#ff6b6b]">
              SPOF
            </span>
          )}
        </div>
      </div>
      <Handle type="source" position={Position.Right} className="!bg-[var(--muted-foreground)]" />
    </div>
  );
}

const nodeTypes = { isms: IsmsNode };

function layout(graph: DependencyGraph, kindLabels: Record<string, string>) {
  const g = new dagre.graphlib.Graph();
  g.setDefaultEdgeLabel(() => ({}));
  g.setGraph({ rankdir: "LR", nodesep: 34, ranksep: 130, marginx: 20, marginy: 20 });
  graph.nodes.forEach((n) => g.setNode(n.id, { width: 200, height: 64 }));
  graph.edges.forEach((e) => g.setEdge(e.source, e.target));
  dagre.layout(g);

  const nodes: Node<NodeData>[] = graph.nodes.map((n) => {
    const p = g.node(n.id);
    return {
      id: n.id,
      type: "isms",
      position: { x: p.x - 100, y: p.y - 32 },
      data: { ...n, dimmed: false, kindLabel: kindLabels[n.kind] ?? n.kind },
    };
  });
  return nodes;
}

function Canvas({
  graph,
  kindLabels,
  labels,
}: {
  graph: DependencyGraph;
  kindLabels: Record<string, string>;
  labels: Record<string, string>;
}) {
  const rf = useReactFlow();
  const [showCritical, setShowCritical] = useState(true);
  const [focus, setFocus] = useState<string | null>(null);
  const [query, setQuery] = useState("");

  const baseNodes = useMemo(() => layout(graph, kindLabels), [graph, kindLabels]);

  // Zusammenhang für Fokus-Highlight (ungerichtet)
  const neighbors = useMemo(() => {
    const m = new Map<string, Set<string>>();
    for (const e of graph.edges) {
      if (!m.has(e.source)) m.set(e.source, new Set());
      if (!m.has(e.target)) m.set(e.target, new Set());
      m.get(e.source)!.add(e.target);
      m.get(e.target)!.add(e.source);
    }
    return m;
  }, [graph.edges]);

  const focusSet = useMemo(() => {
    if (!focus) return null;
    const seen = new Set<string>([focus]);
    const stack = [focus];
    while (stack.length) {
      const cur = stack.pop()!;
      for (const nb of neighbors.get(cur) ?? []) {
        if (!seen.has(nb)) {
          seen.add(nb);
          stack.push(nb);
        }
      }
    }
    return seen;
  }, [focus, neighbors]);

  const nodes: Node<NodeData>[] = useMemo(
    () =>
      baseNodes.map((n) => ({
        ...n,
        data: { ...n.data, dimmed: focusSet ? !focusSet.has(n.id) : false },
      })),
    [baseNodes, focusSet]
  );

  const edges: Edge[] = useMemo(
    () =>
      graph.edges.map((e) => {
        const isCrit = e.critical && showCritical;
        const inFocus = !focusSet || (focusSet.has(e.source) && focusSet.has(e.target));
        return {
          id: e.id,
          source: e.source,
          target: e.target,
          label: e.label,
          type: "smoothstep",
          animated: isCrit,
          className: isCrit ? "critical-edge" : undefined,
          labelStyle: { fill: "#8b93ad", fontSize: 10, fontWeight: 600 },
          labelBgStyle: { fill: "#141a2e" },
          style: {
            stroke: isCrit ? "#ff6b6b" : "#4a5372",
            strokeWidth: isCrit ? 2.5 : 1.5,
            opacity: inFocus ? 1 : 0.12,
          },
        };
      }),
    [graph.edges, showCritical, focusSet]
  );

  const onNodeClick = useCallback((_: unknown, node: Node) => setFocus(node.id), []);
  const onPaneClick = useCallback(() => setFocus(null), []);

  const runSearch = useCallback(
    (q: string) => {
      setQuery(q);
      if (!q.trim()) return;
      const hit = baseNodes.find((n) => n.data.name.toLowerCase().includes(q.toLowerCase()));
      if (hit) {
        setFocus(hit.id);
        rf.setCenter(hit.position.x + 100, hit.position.y + 32, { zoom: 1.2, duration: 500 });
      }
    },
    [baseNodes, rf]
  );

  const exportPng = useCallback(async () => {
    const el = document.querySelector<HTMLElement>(".react-flow__viewport");
    if (!el) return;
    const dataUrl = await toPng(el, {
      backgroundColor: "#0e1220",
      filter: (node) =>
        !(node instanceof HTMLElement && node.classList?.contains("react-flow__controls")),
    });
    const a = document.createElement("a");
    a.href = dataUrl;
    a.download = "abhaengigkeiten.png";
    a.click();
  }, []);

  return (
    <div className="relative h-[600px] overflow-hidden rounded-2xl border border-[var(--panel-brd)] bg-[var(--bg-1,#141a2e)]">
      {/* Toolbar */}
      <div className="absolute top-3 left-3 z-10 flex flex-wrap items-center gap-2">
        <div className="flex items-center gap-2 rounded-lg border border-[var(--panel-brd)] bg-[var(--panel)] px-3 py-1.5 backdrop-blur">
          <Search className="size-4 text-muted-foreground" />
          <input
            value={query}
            onChange={(e) => runSearch(e.target.value)}
            placeholder={labels.search}
            className="w-40 border-0 bg-transparent text-[13px] text-foreground outline-none placeholder:text-muted-foreground"
          />
        </div>
        <button
          onClick={() => setShowCritical((v) => !v)}
          className={cn(
            "inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-[13px] font-semibold backdrop-blur",
            showCritical
              ? "border-[rgba(255,107,107,0.4)] bg-[rgba(255,107,107,0.12)] text-[#ff6b6b]"
              : "border-[var(--panel-brd)] bg-[var(--panel)] text-muted-foreground"
          )}
        >
          {showCritical ? <Zap className="size-4" /> : <ZapOff className="size-4" />}
          {labels.criticalToggle}
        </button>
        <button
          onClick={() => rf.fitView({ duration: 500, padding: 0.15 })}
          title={labels.fit}
          className="inline-flex items-center gap-1.5 rounded-lg border border-[var(--panel-brd)] bg-[var(--panel)] px-3 py-1.5 text-[13px] font-semibold text-muted-foreground backdrop-blur hover:text-foreground"
        >
          <Crosshair className="size-4" /> {labels.fit}
        </button>
        <button
          onClick={exportPng}
          title="PNG"
          className="inline-flex items-center gap-1.5 rounded-lg border border-[var(--panel-brd)] bg-[var(--panel)] px-3 py-1.5 text-[13px] font-semibold text-muted-foreground backdrop-blur hover:text-foreground"
        >
          <Download className="size-4" /> PNG
        </button>
      </div>

      <ReactFlow
        nodes={nodes}
        edges={edges}
        nodeTypes={nodeTypes}
        onNodeClick={onNodeClick}
        onPaneClick={onPaneClick}
        fitView
        fitViewOptions={{ padding: 0.15 }}
        minZoom={0.2}
        proOptions={{ hideAttribution: true }}
      >
        <Background variant={BackgroundVariant.Dots} gap={26} size={1} color="rgba(140,155,200,0.18)" />
        <Controls className="!border-[var(--panel-brd)] !bg-[var(--panel)]" showInteractive={false} />
      </ReactFlow>
    </div>
  );
}

export function DependencyGraphView(props: {
  graph: DependencyGraph;
  kindLabels: Record<string, string>;
  labels: Record<string, string>;
}) {
  return (
    <ReactFlowProvider>
      <Canvas {...props} />
    </ReactFlowProvider>
  );
}
