"use client";

import { useEffect, useRef } from "react";
import type { Game } from "@/game/Game";
import { hasLOS } from "@/game/combat/sim";

const MAX = 6;

interface PlateEls {
  root: HTMLDivElement;
  name: HTMLSpanElement;
  fill: HTMLElement;
  chip: HTMLElement;
  cast: HTMLDivElement;
  castFill: HTMLElement;
  castName: HTMLSpanElement;
  // last written values: the loop only touches the DOM when one changes
  px: number;
  py: number;
  hp: number;
  uid: string;
  castKey: string;
  castPct: number;
  target: boolean;
  hidden: boolean;
  shown: boolean;
}

const pt = { x: 0, y: 0 };

/** Plates over enemy heads, projected from the 3D position every frame. A fixed pool of DOM nodes, no allocation per frame. */
export function Nameplates({ game }: { game: Game }) {
  const host = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = host.current;
    if (!el) return;
    const plates: PlateEls[] = [];
    for (let i = 0; i < MAX; i++) {
      const root = el.children[i] as HTMLDivElement;
      plates.push({
        root,
        name: root.querySelector(".np-name") as HTMLSpanElement,
        fill: root.querySelector(".np-hp > i") as HTMLElement,
        chip: root.querySelector(".np-hp > b") as HTMLElement,
        cast: root.querySelector(".np-cast") as HTMLDivElement,
        castFill: root.querySelector(".np-cast > i") as HTMLElement,
        castName: root.querySelector(".np-cast > span") as HTMLSpanElement,
        px: -1,
        py: -1,
        hp: -1,
        uid: "",
        castKey: "",
        castPct: -1,
        target: false,
        hidden: false,
        shown: false,
      });
    }
    let raf = 0;
    const tick = () => {
      raf = requestAnimationFrame(tick);
      const sim = game.sim;
      const live = game.state === "playing" || game.state === "paused";
      let n = 0;
      if (live) {
        const units = sim.units;
        for (let i = 0; i < units.length && n < MAX; i++) {
          const u = units[i];
          if (u.kind === "player" || u.dead) continue;
          const p = plates[n++];
          const ok = game.projectToScreen(u.x, u.boss ? 2.55 : 2.3, u.z, pt);
          if (!ok) {
            n--;
            continue;
          }
          if (!p.shown) {
            p.root.style.display = "";
            p.shown = true;
          }
          const x = Math.round(pt.x), y = Math.round(pt.y);
          if (x !== p.px || y !== p.py) {
            p.px = x;
            p.py = y;
            p.root.style.transform = `translate3d(${x}px,${y}px,0) translate(-50%,-100%)`;
          }
          if (p.uid !== u.id) {
            p.uid = u.id;
            p.name.textContent = u.name;
            p.hp = -1;
            p.root.classList.toggle("boss", u.boss);
          }
          const pct = Math.max(0, Math.min(100, (u.hp / u.maxHp) * 100));
          if (pct !== p.hp) {
            p.hp = pct;
            p.fill.style.width = `${pct}%`;
            p.chip.style.width = `${pct}%`;
          }
          const hid = !hasLOS(sim.player, u);
          if (hid !== p.hidden) {
            p.hidden = hid;
            p.root.classList.toggle("nosight", hid);
          }
          const tg = sim.targetId === u.id;
          if (tg !== p.target) {
            p.target = tg;
            p.root.classList.toggle("target", tg);
          }
          const c = u.cast;
          if (c) {
            const key = `${c.name}|${c.interruptible ? 1 : 0}`;
            if (key !== p.castKey) {
              p.castKey = key;
              p.cast.style.display = "";
              p.cast.classList.toggle("locked", !c.interruptible);
              p.castName.textContent = c.name;
            }
            const cp = Math.round(Math.max(0, Math.min(100, (c.kind === "channel" ? c.left / c.total : 1 - c.left / c.total) * 100)));
            if (cp !== p.castPct) {
              p.castPct = cp;
              p.castFill.style.width = `${cp}%`;
            }
          } else if (p.castKey) {
            p.castKey = "";
            p.castPct = -1;
            p.cast.style.display = "none";
          }
        }
      }
      for (let i = n; i < MAX; i++) {
        const p = plates[i];
        if (p.shown) {
          p.shown = false;
          p.uid = "";
          p.root.style.display = "none";
        }
      }
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [game]);

  return (
    <div ref={host} className="nameplates" aria-hidden="true">
      {Array.from({ length: MAX }, (_, i) => (
        <div key={i} className="np" style={{ display: "none" }}>
          <span className="np-name" />
          <div className="np-hp">
            <b />
            <i />
          </div>
          <div className="np-cast" style={{ display: "none" }}>
            <i />
            <span />
          </div>
        </div>
      ))}
    </div>
  );
}
