"use client";

import * as React from "react";
import { useEffect, useRef, useCallback } from "react";

interface Node {
  x: number;
  y: number;
  connections: number[];
  pulsePhase: number;
  pulseSpeed: number;
  size: number;
}

interface Trace {
  from: number;
  to: number;
  progress: number;
  speed: number;
  active: boolean;
  delay: number;
}

interface CircuitBoardProps {
  className?: string;
  density?: "low" | "medium" | "high";
  animate?: boolean;
}

export const CircuitBoard: React.FC<CircuitBoardProps> = ({
  className = "",
  density = "medium",
  animate = true,
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const nodesRef = useRef<Node[]>([]);
  const tracesRef = useRef<Trace[]>([]);
  const animationFrameRef = useRef<number>(0);
  const timeRef = useRef<number>(0);
  const dimensionsRef = useRef<{ width: number; height: number }>({
    width: 0,
    height: 0,
  });

  const densityMap = { low: 12, medium: 20, high: 30 };

  const initCircuit = useCallback(
    (width: number, height: number) => {
      const nodeCount = densityMap[density];
      const nodes: Node[] = [];
      const traces: Trace[] = [];
      const gridCols = Math.ceil(Math.sqrt(nodeCount * (width / height)));
      const gridRows = Math.ceil(nodeCount / gridCols);
      const cellW = width / gridCols;
      const cellH = height / gridRows;

      // Create nodes on a jittered grid
      for (let row = 0; row < gridRows; row++) {
        for (let col = 0; col < gridCols; col++) {
          if (nodes.length >= nodeCount) break;
          nodes.push({
            x: cellW * (col + 0.3 + Math.random() * 0.4),
            y: cellH * (row + 0.3 + Math.random() * 0.4),
            connections: [],
            pulsePhase: Math.random() * Math.PI * 2,
            pulseSpeed: 0.5 + Math.random() * 1.5,
            size: 1.5 + Math.random() * 2,
          });
        }
      }

      // Connect nearby nodes with orthogonal traces (PCB style)
      for (let i = 0; i < nodes.length; i++) {
        const maxConnections = 2 + Math.floor(Math.random() * 2);
        const distances: { idx: number; dist: number }[] = [];

        for (let j = 0; j < nodes.length; j++) {
          if (i === j) continue;
          const dx = nodes[j].x - nodes[i].x;
          const dy = nodes[j].y - nodes[i].y;
          distances.push({ idx: j, dist: Math.sqrt(dx * dx + dy * dy) });
        }

        distances.sort((a, b) => a.dist - b.dist);
        let connected = 0;

        for (const d of distances) {
          if (connected >= maxConnections) break;
          if (d.dist > Math.max(cellW, cellH) * 2) break;

          // Avoid duplicate traces
          const exists = traces.some(
            (t) =>
              (t.from === i && t.to === d.idx) ||
              (t.from === d.idx && t.to === i),
          );
          if (exists) continue;

          nodes[i].connections.push(d.idx);
          nodes[d.idx].connections.push(i);
          traces.push({
            from: i,
            to: d.idx,
            progress: 0,
            speed: 0.002 + Math.random() * 0.004,
            active: Math.random() > 0.6,
            delay: Math.random() * 3000,
          });
          connected++;
        }
      }

      nodesRef.current = nodes;
      tracesRef.current = traces;
    },
    [density],
  );

  const drawTrace = useCallback(
    (
      ctx: CanvasRenderingContext2D,
      x1: number,
      y1: number,
      x2: number,
      y2: number,
      alpha: number,
    ) => {
      // Draw orthogonal PCB-style trace (L-shaped)
      const midX = Math.random() > 0.5 ? x2 : x1;
      const midY = Math.random() > 0.5 ? y1 : y2;

      ctx.beginPath();
      ctx.moveTo(x1, y1);
      if (Math.abs(x2 - x1) > Math.abs(y2 - y1)) {
        ctx.lineTo(x2, y1);
        ctx.lineTo(x2, y2);
      } else {
        ctx.lineTo(x1, y2);
        ctx.lineTo(x2, y2);
      }
      ctx.strokeStyle = `rgba(203, 213, 225, ${alpha})`;
      ctx.lineWidth = 0.5;
      ctx.stroke();
    },
    [],
  );

  const animateFrame = useCallback(() => {
    const canvas = canvasRef.current;
    const ctx = canvas?.getContext("2d");
    if (!canvas || !ctx) return;

    const { width, height } = dimensionsRef.current;
    const nodes = nodesRef.current;
    const traces = tracesRef.current;
    timeRef.current += 16;
    const time = timeRef.current;

    ctx.clearRect(0, 0, width, height);

    // Draw static traces
    traces.forEach((trace) => {
      const from = nodes[trace.from];
      const to = nodes[trace.to];
      if (!from || !to) return;

      // Static trace line
      ctx.beginPath();
      ctx.moveTo(from.x, from.y);
      if (Math.abs(to.x - from.x) > Math.abs(to.y - from.y)) {
        ctx.lineTo(to.x, from.y);
        ctx.lineTo(to.x, to.y);
      } else {
        ctx.lineTo(from.x, to.y);
        ctx.lineTo(to.x, to.y);
      }
      ctx.strokeStyle = "rgba(226, 232, 240, 0.4)";
      ctx.lineWidth = 0.5;
      ctx.stroke();

      // Animated data packet traveling along trace
      if (animate && trace.active && time > trace.delay) {
        trace.progress += trace.speed;
        if (trace.progress > 1) {
          trace.progress = 0;
          trace.active = Math.random() > 0.3;
          trace.delay = time + Math.random() * 5000;
        }

        const p = trace.progress;
        let px: number, py: number;

        if (Math.abs(to.x - from.x) > Math.abs(to.y - from.y)) {
          if (p < 0.5) {
            px = from.x + (to.x - from.x) * (p * 2);
            py = from.y;
          } else {
            px = to.x;
            py = from.y + (to.y - from.y) * ((p - 0.5) * 2);
          }
        } else {
          if (p < 0.5) {
            px = from.x;
            py = from.y + (to.y - from.y) * (p * 2);
          } else {
            px = from.x + (to.x - from.x) * ((p - 0.5) * 2);
            py = to.y;
          }
        }

        // Data packet glow
        const gradient = ctx.createRadialGradient(px, py, 0, px, py, 8);
        gradient.addColorStop(0, "rgba(148, 163, 184, 0.6)");
        gradient.addColorStop(1, "rgba(148, 163, 184, 0)");
        ctx.fillStyle = gradient;
        ctx.fillRect(px - 8, py - 8, 16, 16);

        // Data packet dot
        ctx.beginPath();
        ctx.arc(px, py, 1.5, 0, Math.PI * 2);
        ctx.fillStyle = "rgba(148, 163, 184, 0.8)";
        ctx.fill();
      }
    });

    // Draw nodes
    nodes.forEach((node) => {
      const pulse = animate
        ? 0.3 + Math.sin(time * 0.001 * node.pulseSpeed + node.pulsePhase) * 0.2
        : 0.4;

      // Node glow
      const gradient = ctx.createRadialGradient(
        node.x,
        node.y,
        0,
        node.x,
        node.y,
        node.size * 4,
      );
      gradient.addColorStop(0, `rgba(191, 203, 219, ${pulse * 0.3})`);
      gradient.addColorStop(1, "rgba(191, 203, 219, 0)");
      ctx.fillStyle = gradient;
      ctx.fillRect(
        node.x - node.size * 4,
        node.y - node.size * 4,
        node.size * 8,
        node.size * 8,
      );

      // Node dot
      ctx.beginPath();
      ctx.arc(node.x, node.y, node.size, 0, Math.PI * 2);
      ctx.fillStyle = `rgba(203, 213, 225, ${pulse + 0.2})`;
      ctx.fill();
    });

    // Draw subtle binary text near some nodes
    if (animate) {
      ctx.font = "9px ui-monospace, monospace";
      nodes.forEach((node, i) => {
        if (i % 4 !== 0) return; // Only every 4th node
        const binaryAlpha =
          0.06 + Math.sin(time * 0.0008 + node.pulsePhase) * 0.04;
        ctx.fillStyle = `rgba(148, 163, 184, ${binaryAlpha})`;
        const binary = ((time * 0.01 + i * 137) % 256)
          .toString(2)
          .padStart(8, "0");
        ctx.fillText(binary, node.x + node.size * 3, node.y + 3);
      });
    }

    animationFrameRef.current = requestAnimationFrame(animateFrame);
  }, [animate]);

  const handleResize = useCallback(() => {
    const container = containerRef.current;
    const canvas = canvasRef.current;
    if (!container || !canvas) return;

    const rect = container.getBoundingClientRect();
    const dpr = window.devicePixelRatio || 1;

    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    canvas.style.width = `${rect.width}px`;
    canvas.style.height = `${rect.height}px`;

    const ctx = canvas.getContext("2d");
    if (ctx) ctx.scale(dpr, dpr);

    dimensionsRef.current = { width: rect.width, height: rect.height };
    initCircuit(rect.width, rect.height);
  }, [initCircuit]);

  useEffect(() => {
    handleResize();
    window.addEventListener("resize", handleResize);
    animationFrameRef.current = requestAnimationFrame(animateFrame);

    return () => {
      window.removeEventListener("resize", handleResize);
      cancelAnimationFrame(animationFrameRef.current);
    };
  }, [handleResize, animateFrame]);

  return (
    <div
      ref={containerRef}
      className={`absolute inset-0 pointer-events-none ${className}`}
      aria-hidden="true"
    >
      <canvas ref={canvasRef} className="w-full h-full" />
    </div>
  );
};
