import { useMemo, useState } from "react";

export type HexResonanceSignal =
  | "hearts"
  | "stars"
  | "moons"
  | "hourglasses"
  | "balloons"
  | "hourglass"
  | "balloon";

export interface HexNode {
  id: string;
  ring: number;
  position: number;
  label?: string;
  type?: "you" | "person" | "dim" | "empty" | "resonance-hot" | "resonance-warm" | "resonance-cool";
  avatarUrl?: string;
  resonanceSignal?: HexResonanceSignal;
}

interface HexLatticeProps {
  nodes: HexNode[];
  onNodeClick?: (node: HexNode) => void;
  activeNodeId?: string;
  showLabels?: boolean;
  size?: "compact" | "medium" | "full";
  centerLabel?: string;
  focusRing?: number;
}

type Cube = { x: number; y: number; z: number };
type Axial = { q: number; r: number };

type VisualState = {
  fill: string;
  stroke: string;
  opacity: number;
  strokeWidth: number;
  strokeDasharray?: string;
  primaryText?: string;
  secondaryText?: string;
  primaryFill: string;
  secondaryFill: string;
  glow: string;
  title: string;
  ariaLabel: string;
};

const RING_COLORS: Record<number, string> = {
  0: "#d4a574",
  1: "#e8c4a0",
  2: "#a8d4a8",
  3: "#7eb8c9",
  4: "#c9a8d4",
  5: "#d4a8b4",
  6: "#8a9ba8",
};

const RING_META: Record<number, { name: string; tempo: string }> = {
  0: { name: "YOU", tempo: "Fixed" },
  1: { name: "Inner Circle", tempo: "Yearly" },
  2: { name: "Archetypes", tempo: "Quarterly" },
  3: { name: "The Longing", tempo: "Monthly" },
  4: { name: "The Echo", tempo: "Weekly" },
  5: { name: "The Wave", tempo: "Daily" },
  6: { name: "The Current", tempo: "Hourly" },
};

const SIGNAL_META: Record<Exclude<HexResonanceSignal, "hourglass" | "balloon">, { icon: string; color: string; label: string }> = {
  hearts: { icon: "❤", color: "#ff6b8a", label: "Hearts" },
  stars: { icon: "⭐", color: "#ffd93d", label: "Stars" },
  moons: { icon: "☾", color: "#a8d4ff", label: "Moons" },
  hourglasses: { icon: "⏳", color: "#c9a8d4", label: "Hourglasses" },
  balloons: { icon: "🎈", color: "#ff9f43", label: "Balloons" },
};

const WARMTH_META: Record<"hot" | "warm" | "cool", { icon: string; color: string; label: string }> = {
  hot: { icon: "🔥", color: "#fb7185", label: "Hot" },
  warm: { icon: "🌡️", color: "#f59e0b", label: "Warm" },
  cool: { icon: "🌿", color: "#94a3b8", label: "Cool" },
};

const CUBE_DIRECTIONS: Cube[] = [
  { x: 1, y: -1, z: 0 },
  { x: 1, y: 0, z: -1 },
  { x: 0, y: 1, z: -1 },
  { x: -1, y: 1, z: 0 },
  { x: -1, y: 0, z: 1 },
  { x: 0, y: -1, z: 1 },
];

function cubeAdd(a: Cube, b: Cube): Cube {
  return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
}

function cubeScale(cube: Cube, factor: number): Cube {
  return { x: cube.x * factor, y: cube.y * factor, z: cube.z * factor };
}

function cubeToAxial(cube: Cube): Axial {
  return { q: cube.x, r: cube.z };
}

function axialToPixel(q: number, r: number, size: number): { x: number; y: number } {
  return {
    x: size * Math.sqrt(3) * (q + r / 2),
    y: size * 1.5 * r,
  };
}

function hexCorners(cx: number, cy: number, size: number): string {
  const points: string[] = [];
  for (let i = 0; i < 6; i += 1) {
    const angle = (Math.PI / 3) * i - Math.PI / 6;
    points.push(`${cx + size * Math.cos(angle)},${cy + size * Math.sin(angle)}`);
  }
  return points.join(" ");
}

function getRingPositions(ring: number): Axial[] {
  if (ring === 0) return [{ q: 0, r: 0 }];

  let cube = cubeAdd(cubeScale(CUBE_DIRECTIONS[4], ring), { x: 0, y: 0, z: 0 });
  const positions: Axial[] = [];

  for (let side = 0; side < 6; side += 1) {
    for (let step = 0; step < ring; step += 1) {
      positions.push(cubeToAxial(cube));
      cube = cubeAdd(cube, CUBE_DIRECTIONS[side]);
    }
  }

  return positions;
}

function getHexSize(size: NonNullable<HexLatticeProps["size"]>) {
  switch (size) {
    case "compact":
      return 28;
    case "medium":
      return 38;
    case "full":
      return 52;
  }
}

function normalizeSignalKey(signal: HexResonanceSignal): keyof typeof SIGNAL_META {
  if (signal === "hourglass") return "hourglasses";
  if (signal === "balloon") return "balloons";
  return signal;
}

function getNodeVisual(
  node: HexNode | undefined,
  ring: number,
  position: number,
  centerLabel: string,
): VisualState {
  const ringColor = RING_COLORS[ring];
  const ringName = RING_META[ring].name;
  const slotName = position + 1;

  if (!node) {
    return {
      fill: ring === 0 ? ringColor : `${ringColor}22`,
      stroke: ringColor,
      opacity: ring === 0 ? 1 : 0.45,
      strokeWidth: ring === 0 ? 1.5 : 1,
      primaryText: ring === 0 ? centerLabel : undefined,
      secondaryText: ring === 0 ? "CENTER" : undefined,
      primaryFill: "#fff7ed",
      secondaryFill: "#d7c7b5",
      glow: ring === 0 ? "drop-shadow(0 0 16px rgba(212, 165, 116, 0.35))" : "none",
      title: `${ringName} · empty slot ${slotName}`,
      ariaLabel: `${ringName} empty slot ${slotName}`,
    };
  }

  if (node.type === "you") {
    return {
      fill: ringColor,
      stroke: "#fff1c1",
      opacity: 1,
      strokeWidth: 2,
      primaryText: node.label || centerLabel,
      secondaryText: "CENTER",
      primaryFill: "#fff8e8",
      secondaryFill: "#f0d8bb",
      glow: "drop-shadow(0 0 18px rgba(255, 236, 184, 0.32))",
      title: `${ringName} · fixed center`,
      ariaLabel: `${ringName} fixed center`,
    };
  }

  if (node.type === "dim") {
    return {
      fill: "#222228",
      stroke: "#4b5563",
      opacity: 0.45,
      strokeWidth: 1.15,
      strokeDasharray: "5 4",
      primaryText: node.label || `Dim ${slotName}`,
      secondaryText: "dormant",
      primaryFill: "#cbd5e1",
      secondaryFill: "#94a3b8",
      glow: "none",
      title: `${ringName} · dim slot ${slotName}`,
      ariaLabel: `${ringName} dim slot ${slotName}`,
    };
  }

  if (node.type === "empty") {
    return {
      fill: "transparent",
      stroke: "#5b6473",
      opacity: 0.36,
      strokeWidth: 1,
      strokeDasharray: "4 4",
      primaryText: node.label || undefined,
      secondaryText: node.label ? "empty" : undefined,
      primaryFill: "#d7dee8",
      secondaryFill: "#94a3b8",
      glow: "none",
      title: `${ringName} · empty slot ${slotName}`,
      ariaLabel: `${ringName} empty slot ${slotName}`,
    };
  }

  if (node.type === "resonance-hot") {
    const warmth = WARMTH_META.hot;
    return {
      fill: "rgba(251, 113, 133, 0.18)",
      stroke: warmth.color,
      opacity: 1,
      strokeWidth: 1.35,
      primaryText: node.label || warmth.label.toUpperCase(),
      secondaryText: warmth.icon,
      primaryFill: "#ffe4ea",
      secondaryFill: warmth.color,
      glow: "drop-shadow(0 0 14px rgba(251, 113, 133, 0.24))",
      title: `${ringName} · ${warmth.label}`,
      ariaLabel: `${ringName} ${warmth.label}`,
    };
  }

  if (node.type === "resonance-warm") {
    const warmth = WARMTH_META.warm;
    return {
      fill: "rgba(245, 158, 11, 0.14)",
      stroke: warmth.color,
      opacity: 1,
      strokeWidth: 1.35,
      primaryText: node.label || warmth.label.toUpperCase(),
      secondaryText: warmth.icon,
      primaryFill: "#fff1d6",
      secondaryFill: warmth.color,
      glow: "drop-shadow(0 0 14px rgba(245, 158, 11, 0.24))",
      title: `${ringName} · ${warmth.label}`,
      ariaLabel: `${ringName} ${warmth.label}`,
    };
  }

  if (node.type === "resonance-cool") {
    const warmth = WARMTH_META.cool;
    return {
      fill: "rgba(148, 163, 184, 0.14)",
      stroke: warmth.color,
      opacity: 1,
      strokeWidth: 1.25,
      primaryText: node.label || warmth.label.toUpperCase(),
      secondaryText: warmth.icon,
      primaryFill: "#e2e8f0",
      secondaryFill: warmth.color,
      glow: "drop-shadow(0 0 12px rgba(148, 163, 184, 0.18))",
      title: `${ringName} · ${warmth.label}`,
      ariaLabel: `${ringName} ${warmth.label}`,
    };
  }

  const signal = node.resonanceSignal ? SIGNAL_META[normalizeSignalKey(node.resonanceSignal)] : undefined;

  return {
    fill: `${ringColor}33`,
    stroke: signal?.color || ringColor,
    opacity: 1,
    strokeWidth: signal ? 1.35 : 1.1,
    primaryText: node.label || signal?.label,
    secondaryText: signal ? signal.icon : undefined,
    primaryFill: signal?.color || "#f4eadb",
    secondaryFill: signal?.color || "#d7c7b5",
    glow: signal ? `drop-shadow(0 0 12px ${signal.color}33)` : "none",
    title: `${ringName} · ${node.label || signal?.label || `slot ${slotName}`}`,
    ariaLabel: `${ringName} ${node.label || signal?.label || `slot ${slotName}`}`,
  };
}

export default function HexLattice({
  nodes,
  onNodeClick,
  activeNodeId,
  showLabels = false,
  size = "medium",
  centerLabel = "YOU",
  focusRing,
}: HexLatticeProps) {
  const [hoveredNodeId, setHoveredNodeId] = useState<string | null>(null);

  const layout = useMemo(() => {
    const hexRadius = getHexSize(size);
    const gridRadius = hexRadius * 1.03;
    const nodeMap = new Map(nodes.map((node) => [`${node.ring}-${node.position}`, node]));
    const activeNode = nodes.find((node) => node.id === activeNodeId);
    const activeRing = activeNode?.ring;
    const emphasizedRing = focusRing ?? activeRing;

    const items = [] as Array<{
      id: string;
      ring: number;
      position: number;
      x: number;
      y: number;
      node?: HexNode;
      visual: VisualState;
      isActive: boolean;
      isHovered: boolean;
      isEmphasized: boolean;
    }>;

    for (let ring = 0; ring <= 6; ring += 1) {
      const positions = getRingPositions(ring);
      positions.forEach((coord, position) => {
        const point = axialToPixel(coord.q, coord.r, gridRadius);
        const node = nodeMap.get(`${ring}-${position}`);
        const id = node?.id || `ring-${ring}-slot-${position}`;
        const visual = getNodeVisual(node, ring, position, centerLabel);
        const isActive = activeNodeId === id;
        const isHovered = hoveredNodeId === id;
        const isEmphasized = emphasizedRing !== undefined ? ring === emphasizedRing : false;

        items.push({
          id,
          ring,
          position,
          x: point.x,
          y: point.y,
          node,
          visual,
          isActive,
          isHovered,
          isEmphasized,
        });
      });
    }

    const extents = items.map((item) => ({
      minX: item.x - hexRadius,
      maxX: item.x + hexRadius,
      minY: item.y - hexRadius,
      maxY: item.y + hexRadius,
    }));

    const minX = Math.min(...extents.map((value) => value.minX));
    const maxX = Math.max(...extents.map((value) => value.maxX));
    const minY = Math.min(...extents.map((value) => value.minY));
    const maxY = Math.max(...extents.map((value) => value.maxY));
    const padding = hexRadius * 1.25;

    return {
      items,
      hexRadius,
      padding,
      width: maxX - minX + padding * 2,
      height: maxY - minY + padding * 2,
      offsetX: -minX + padding,
      offsetY: -minY + padding,
    };
  }, [activeNodeId, centerLabel, focusRing, hoveredNodeId, nodes, size]);

  const handleNodeClick = (node?: HexNode) => {
    if (node) onNodeClick?.(node);
  };

  return (
    <div className="hex-lattice-container w-full overflow-visible rounded-2xl bg-[#0d0d12] p-4">
      <svg
        viewBox={`0 0 ${layout.width} ${layout.height}`}
        role="img"
        aria-label="Cascade hex lattice"
        className="block h-auto w-full overflow-visible"
        style={{ overflow: "visible" }}
      >
        {layout.items.map((item) => {
          const cx = item.x + layout.offsetX;
          const cy = item.y + layout.offsetY;
          const polygonSize = layout.hexRadius * 0.94;
          const strokeWidth = item.isActive ? 2.5 : item.isHovered ? 1.8 : item.visual.strokeWidth;
          const polygonFill = item.isActive && item.visual.fill !== "transparent" ? `${item.visual.fill}` : item.visual.fill;
          const primaryY = item.visual.secondaryText ? cy - layout.hexRadius * 0.1 : cy;
          const secondaryY = cy + layout.hexRadius * 0.22;
          const primarySize = layout.hexRadius * 0.23;
          const secondarySize = layout.hexRadius * 0.17;
          const click = item.node ? () => handleNodeClick(item.node) : undefined;
          const hoverColor = item.visual.stroke;
          const ringFocus = item.isEmphasized && item.ring !== 0;
          const hoverGlow = item.isActive || item.isHovered || ringFocus ? `drop-shadow(0 0 16px ${hoverColor}55)` : item.visual.glow;
          const opacity = item.visual.opacity * (item.isEmphasized || !layout.items.some((candidate) => candidate.isEmphasized) ? 1 : 0.58);

          return (
            <g
              key={item.id}
              onMouseEnter={() => setHoveredNodeId(item.id)}
              onMouseLeave={() => setHoveredNodeId(null)}
              onClick={click}
              style={{ cursor: item.node ? "pointer" : "default", opacity }}
            >
              <title>{item.visual.title}</title>
              <polygon
                points={hexCorners(cx, cy, polygonSize)}
                fill={polygonFill}
                stroke={item.isActive ? "#fff" : item.isHovered ? item.visual.stroke : item.visual.stroke}
                strokeWidth={strokeWidth}
                strokeDasharray={item.visual.strokeDasharray}
                style={{ transition: "all 160ms ease", filter: hoverGlow }}
              />
              {showLabels && item.visual.primaryText && (
                <text
                  x={cx}
                  y={primaryY}
                  textAnchor="middle"
                  dominantBaseline="middle"
                  fill={item.isActive ? "#fff8e7" : item.visual.primaryFill}
                  fontSize={primarySize}
                  fontWeight={600}
                  letterSpacing="0.02em"
                  style={{ pointerEvents: "none" }}
                >
                  {item.visual.primaryText}
                </text>
              )}
              {showLabels && item.visual.secondaryText && (
                <text
                  x={cx}
                  y={secondaryY}
                  textAnchor="middle"
                  dominantBaseline="middle"
                  fill={item.isActive ? "#fff1c1" : item.visual.secondaryFill}
                  fontSize={secondarySize}
                  fontWeight={700}
                  style={{ pointerEvents: "none" }}
                >
                  {item.visual.secondaryText}
                </text>
              )}
              {item.ring === 0 && !showLabels && (
                <text
                  x={cx}
                  y={cy}
                  textAnchor="middle"
                  dominantBaseline="middle"
                  fill="#fff8e8"
                  fontSize={layout.hexRadius * 0.27}
                  fontWeight={700}
                  style={{ pointerEvents: "none" }}
                >
                  {centerLabel}
                </text>
              )}
            </g>
          );
        })}
      </svg>
    </div>
  );
}
