"use client";

import { useEffect, useRef, useState } from "react";
import * as d3 from "d3";

interface GraphNode {
  id: string;
  name: string;
  type: string;
  file: string;
  x?: number;
  y?: number;
  fx?: number | null;
  fy?: number | null;
}

interface GraphEdge {
  source: string | GraphNode;
  target: string | GraphNode;
  type: string;
}

const TYPE_COLORS: Record<string, string> = {
  Function: "#6366f1",
  Class: "#22c55e",
  Method: "#eab308",
};

const EDGE_COLORS: Record<string, string> = {
  CALLS: "#6366f1",
  IMPORTS: "#22c55e",
  EXTENDS: "#ef4444",
  HAS_METHOD: "#06b6d4",
  DEFINED_IN: "#1e1e2e",
};

interface Props {
  onSelectNode: (uid: string) => void;
}

export function GraphViewer({ onSelectNode }: Props) {
  const svgRef = useRef<SVGSVGElement>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch("/api/graph?limit=150")
      .then((r) => r.json())
      .then((data) => {
        setLoading(false);
        if (!svgRef.current || !data.nodes?.length) return;
        renderGraph(svgRef.current, data.nodes, data.edges, onSelectNode);
      })
      .catch(() => setLoading(false));
  }, [onSelectNode]);

  if (loading) {
    return (
      <div className="flex flex-col items-center justify-center h-full gap-3">
        <div className="w-10 h-10 rounded-full border-2 border-t-transparent pulse-glow" style={{ borderColor: "var(--accent)", borderTopColor: "transparent" }} />
        <span className="text-xs" style={{ color: "var(--text-dim)" }}>Loading graph...</span>
      </div>
    );
  }

  return <svg ref={svgRef} className="w-full h-full" style={{ background: "var(--bg)" }} />;
}

function renderGraph(
  svg: SVGSVGElement,
  nodes: GraphNode[],
  edges: GraphEdge[],
  onSelect: (uid: string) => void
) {
  const width = svg.clientWidth;
  const height = svg.clientHeight;

  d3.select(svg).selectAll("*").remove();

  const root = d3.select(svg);

  // Glow filter
  const defs = root.append("defs");
  const filter = defs.append("filter").attr("id", "glow");
  filter.append("feGaussianBlur").attr("stdDeviation", "3").attr("result", "blur");
  filter.append("feMerge").selectAll("feMergeNode")
    .data(["blur", "SourceGraphic"])
    .join("feMergeNode")
    .attr("in", (d) => d);

  const g = root.append("g");
  root.call(
    d3.zoom<SVGSVGElement, unknown>()
      .scaleExtent([0.1, 4])
      .on("zoom", (event) => g.attr("transform", event.transform))
  );

  const simulation = d3
    .forceSimulation(nodes)
    .force(
      "link",
      d3.forceLink<GraphNode, GraphEdge>(edges)
        .id((d) => d.id)
        .distance(80)
    )
    .force("charge", d3.forceManyBody().strength(-200))
    .force("center", d3.forceCenter(width / 2, height / 2))
    .force("collision", d3.forceCollide(25));

  // Edges
  const link = g
    .append("g")
    .selectAll("line")
    .data(edges)
    .join("line")
    .attr("stroke", (d) => EDGE_COLORS[d.type] || "#1e1e2e")
    .attr("stroke-opacity", 0.3)
    .attr("stroke-width", 1);

  // Node groups
  const node = g
    .append("g")
    .selectAll("g")
    .data(nodes)
    .join("g")
    .style("cursor", "pointer")
    .on("click", (_, d) => onSelect(d.id))
    .on("mouseenter", function () {
      d3.select(this).select("circle").attr("filter", "url(#glow)");
      d3.select(this).select("text").attr("fill", "#e4e4ef");
    })
    .on("mouseleave", function () {
      d3.select(this).select("circle").attr("filter", null);
      d3.select(this).select("text").attr("fill", "#6b6b80");
    })
    .call(
      d3.drag<SVGGElement, GraphNode>()
        .on("start", (event, d) => {
          if (!event.active) simulation.alphaTarget(0.3).restart();
          d.fx = d.x;
          d.fy = d.y;
        })
        .on("drag", (event, d) => {
          d.fx = event.x;
          d.fy = event.y;
        })
        .on("end", (event, d) => {
          if (!event.active) simulation.alphaTarget(0);
          d.fx = null;
          d.fy = null;
        }) as any
    );

  // Outer glow ring for Class nodes
  node
    .filter((d) => d.type === "Class")
    .append("circle")
    .attr("r", 14)
    .attr("fill", "none")
    .attr("stroke", (d) => TYPE_COLORS[d.type] || "#666")
    .attr("stroke-opacity", 0.15)
    .attr("stroke-width", 2);

  // Node circles
  node
    .append("circle")
    .attr("r", (d) => (d.type === "Class" ? 8 : 5))
    .attr("fill", (d) => TYPE_COLORS[d.type] || "#666");

  // Node labels
  node
    .append("text")
    .text((d) => d.name)
    .attr("x", (d) => (d.type === "Class" ? 18 : 12))
    .attr("y", 4)
    .attr("font-size", "10px")
    .attr("font-family", "Inter, sans-serif")
    .attr("fill", "#6b6b80");

  simulation.on("tick", () => {
    link
      .attr("x1", (d: any) => d.source.x)
      .attr("y1", (d: any) => d.source.y)
      .attr("x2", (d: any) => d.target.x)
      .attr("y2", (d: any) => d.target.y);

    node.attr("transform", (d) => `translate(${d.x}, ${d.y})`);
  });
}
