"use client";

import { useEffect, useRef, type MutableRefObject, type RefObject } from "react";
import { ArrowCounterClockwise, Cursor, Eraser, Highlighter, PencilSimple, X } from "@phosphor-icons/react";

import { areaDaImagem, CORES_DA_CANETA, novoIdDeRisco, type Ferramenta, type QuadroDeAnotacoes, type RecadoDaCaneta } from "@/lib/live/anotacoes";

/*
 * Os destaques em cima da tela compartilhada. Quem assiste só vê; quem
 * apresenta, com uma ferramenta escolhida, risca com o mouse (ou o dedo).
 * O desenho acompanha a imagem da tela, não a janela.
 */

export type EscolhaDaCaneta = { ferramenta: Ferramenta; cor: string };

/*
 * De quanto em quanto tempo os pontos saem pelo canal (o desenho aqui é na
 * hora; quem assiste recebe em lotes). 8 por segundo: o canal da Supabase
 * limita mensagens por segundo e mais que isso não melhora o que se vê.
 */
const INTERVALO_DE_ENVIO_MS = 125;
/* Nitidez da camada: acima disso a tela grande fica pesada sem ganho visível. */
const NITIDEZ_MAXIMA = 1.5;

export default function CamadaDeDestaques({
  videoRef,
  quadro,
  escolha,
  riscar,
}: {
  videoRef: RefObject<HTMLVideoElement | null>;
  quadro: MutableRefObject<QuadroDeAnotacoes>;
  escolha: EscolhaDaCaneta | null;
  riscar: (recado: RecadoDaCaneta, aplicarAqui?: boolean) => void;
}) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const traco = useRef<{ id: string; f: "caneta" | "marca"; c: string; pendentes: number[]; enviados: number } | null>(null);
  const ultimoLaser = useRef(0);
  /* O tamanho e a posição da camada, medidos só quando mudam (medir a cada quadro deixava tudo lento). */
  const caixa = useRef({ x: 0, y: 0, l: 0, a: 0 });

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const medir = () => {
      const r = canvas.getBoundingClientRect();
      caixa.current = { x: r.left, y: r.top, l: r.width, a: r.height };
    };
    medir();
    const observador = new ResizeObserver(medir);
    observador.observe(canvas);
    window.addEventListener("resize", medir);
    window.addEventListener("scroll", medir, true);
    return () => {
      observador.disconnect();
      window.removeEventListener("resize", medir);
      window.removeEventListener("scroll", medir, true);
    };
  }, []);

  /*
   * Pinta só quando muda alguma coisa (risco novo, tamanho, ou o laser
   * sumindo). Sem nada mudando, o laço só compara números: não mede a tela
   * nem redesenha (antes redesenhava e media 60 vezes por segundo).
   */
  useEffect(() => {
    let quadroAnimado = 0;
    let versao = -1;
    let tamanho = "";
    let tinhaLaser = false;
    const pintar = () => {
      quadroAnimado = requestAnimationFrame(pintar);
      const canvas = canvasRef.current;
      const video = videoRef.current;
      if (!canvas || !video) return;
      const q = quadro.current;
      const { l, a } = caixa.current;
      const agoraTamanho = `${Math.round(l)}x${Math.round(a)}x${video.videoWidth}x${video.videoHeight}`;
      const laser = q.temLaser();
      if (versao === q.versao && tamanho === agoraTamanho && !laser && !tinhaLaser) return;
      tinhaLaser = laser;
      versao = q.versao;
      tamanho = agoraTamanho;
      const dpr = Math.min(NITIDEZ_MAXIMA, window.devicePixelRatio || 1);
      const largura = Math.round(l * dpr);
      const altura = Math.round(a * dpr);
      if (canvas.width !== largura || canvas.height !== altura) {
        canvas.width = largura;
        canvas.height = altura;
      }
      const ctx = canvas.getContext("2d");
      if (!ctx) return;
      ctx.setTransform(1, 0, 0, 1, 0, 0);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      if (q.vazio) return;
      ctx.scale(dpr, dpr);
      q.desenhar(ctx, areaDaImagem(l, a, video.videoWidth, video.videoHeight));
    };
    quadroAnimado = requestAnimationFrame(pintar);
    return () => cancelAnimationFrame(quadroAnimado);
  }, [quadro, videoRef]);

  /* Os pontos do risco saem em lotes. */
  useEffect(() => {
    if (!escolha) return;
    const relogio = setInterval(() => {
      const t = traco.current;
      if (!t || !t.pendentes.length) return;
      const p = t.pendentes.splice(0);
      riscar({ k: "r", id: t.id, f: t.f, c: t.c, p, de: t.enviados }, false);
      t.enviados += p.length;
    }, INTERVALO_DE_ENVIO_MS);
    return () => clearInterval(relogio);
  }, [escolha, riscar]);

  const ponto = (e: React.PointerEvent<HTMLCanvasElement>): [number, number] | null => {
    const video = videoRef.current;
    if (!video) return null;
    const c = caixa.current;
    const area = areaDaImagem(c.l, c.a, video.videoWidth, video.videoHeight);
    const x = (e.clientX - c.x - area.x) / area.l;
    const y = (e.clientY - c.y - area.y) / area.a;
    if (x < -0.02 || x > 1.02 || y < -0.02 || y > 1.02) return null;
    return [Math.round(Math.min(1, Math.max(0, x)) * 10000) / 10000, Math.round(Math.min(1, Math.max(0, y)) * 10000) / 10000];
  };

  const terminarTraco = () => {
    const t = traco.current;
    if (!t) return;
    traco.current = null;
    riscar({ k: "r", id: t.id, f: t.f, c: t.c, p: t.pendentes.splice(0), de: t.enviados, fim: true }, false);
  };

  return (
    <canvas
      ref={canvasRef}
      className={`lv-destaques${escolha ? ` ativa ${escolha.ferramenta}` : ""}`}
      aria-hidden={!escolha}
      onPointerDown={(e) => {
        if (!escolha || escolha.ferramenta === "laser") return;
        const p = ponto(e);
        if (!p) return;
        e.currentTarget.setPointerCapture(e.pointerId);
        const f = escolha.ferramenta === "marca" ? "marca" : "caneta";
        const c = f === "marca" ? "#fbbc04" : escolha.cor;
        traco.current = { id: novoIdDeRisco(), f, c, pendentes: [...p], enviados: 0 };
        quadro.current.aplicar({ k: "r", id: traco.current.id, f, c, p });
      }}
      onPointerMove={(e) => {
        if (!escolha) return;
        const p = ponto(e);
        if (escolha.ferramenta === "laser") {
          if (!p) return;
          quadro.current.aplicar({ k: "l", p });
          const agora = Date.now();
          if (agora - ultimoLaser.current >= INTERVALO_DE_ENVIO_MS) {
            ultimoLaser.current = agora;
            riscar({ k: "l", p }, false);
          }
          return;
        }
        const t = traco.current;
        if (!t || !p) return;
        t.pendentes.push(...p);
        quadro.current.aplicar({ k: "r", id: t.id, f: t.f, c: t.c, p });
      }}
      onPointerUp={terminarTraco}
      onPointerCancel={terminarTraco}
      onPointerLeave={() => {
        if (escolha?.ferramenta === "laser") riscar({ k: "l", p: [] });
      }}
    />
  );
}

/* A barrinha das ferramentas, em cima da tela de quem apresenta. */
export function BarraDeDestaques({
  escolha,
  aoEscolher,
  quadro,
  riscar,
}: {
  escolha: EscolhaDaCaneta;
  aoEscolher: (escolha: EscolhaDaCaneta | null) => void;
  quadro: MutableRefObject<QuadroDeAnotacoes>;
  riscar: (recado: RecadoDaCaneta, aplicarAqui?: boolean) => void;
}) {
  const ferramentas: Array<{ f: Ferramenta; rotulo: string; Icone: typeof Cursor }> = [
    { f: "laser", rotulo: "Ponteiro laser", Icone: Cursor },
    { f: "caneta", rotulo: "Caneta", Icone: PencilSimple },
    { f: "marca", rotulo: "Marca-texto", Icone: Highlighter },
  ];
  return (
    <div className="lv-ferramentas" role="toolbar" aria-label="Destacar na tela">
      {ferramentas.map(({ f, rotulo, Icone }) => (
        <button key={f} type="button" className={escolha.ferramenta === f ? "on" : ""} onClick={() => aoEscolher({ ...escolha, ferramenta: f })} title={rotulo} aria-label={rotulo}>
          <Icone weight={escolha.ferramenta === f ? "fill" : "regular"} />
        </button>
      ))}
      {escolha.ferramenta === "caneta" ? (
        <span className="lv-cores">
          {CORES_DA_CANETA.map((cor) => (
            <button
              key={cor}
              type="button"
              className={escolha.cor === cor ? "on" : ""}
              style={{ background: cor }}
              onClick={() => aoEscolher({ ...escolha, cor })}
              aria-label={`Cor ${cor}`}
            />
          ))}
        </span>
      ) : null}
      <span className="lv-ferramentas-sep" />
      <button
        type="button"
        title="Desfazer"
        aria-label="Desfazer"
        onClick={() => {
          const id = quadro.current.ultimoRisco();
          if (id) riscar({ k: "desfazer", id });
        }}
      >
        <ArrowCounterClockwise />
      </button>
      <button type="button" title="Apagar tudo" aria-label="Apagar tudo" onClick={() => riscar({ k: "limpar" })}>
        <Eraser />
      </button>
      <button type="button" title="Fechar as ferramentas" aria-label="Fechar as ferramentas" onClick={() => aoEscolher(null)}>
        <X />
      </button>
    </div>
  );
}
