"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { Download, Loader2, RotateCcw, Trash2, Zap } from "lucide-react";
import { BotaoContaGotas, Chave, Cor, Opcoes, Regua } from "./campos";
import { baixarArquivo, carregarFoto, paletaDaFoto, pngParaLaser } from "./tratar-foto";
import { ESTILOS_DA_RETICULA, MEIO_TOM_LASER, MEIO_TOM_PADRAO, hexDoRgb, pontilharPixels, reticulaEmPixels, reticulaEmSvg, type EstiloDaReticula, type MeioTom, type PaletaDaFoto, type TratamentoDaFoto, type TrocaDeCor } from "@/lib/banner-editor/tratamento-da-foto";
import type { PecaDeImagem } from "@/lib/banner-editor/documento";

/*
  Os painéis do tratamento da foto: "Trocar cores" (as bolinhas das cores
  que compõem a imagem e o "Substituir cor" com o conta-gotas, como no
  Corel) e "Retícula" (o meio-tom em vários estilos, com o que a gravação
  a laser pede: preto puro, papel branco, o SVG em vetor e o PNG com DPI).
  Cada mudança vai para o editor (`aoTratar`), que mostra a prévia na hora
  e guarda o arquivo quando a pessoa para de mexer.
*/

function Secao({ titulo, children, acao }: { titulo: string; children: React.ReactNode; acao?: React.ReactNode }) {
  return (
    <section className="mb-6">
      <div className="mb-2.5 flex items-center justify-between gap-2">
        <h3 className="text-[13px] font-bold text-[#1F1B2E]">{titulo}</h3>
        {acao}
      </div>
      {children}
    </section>
  );
}

function Aplicando({ aplicando }: { aplicando: boolean }) {
  return aplicando ? (
    <p className="mb-4 flex items-center gap-2 rounded-[10px] bg-[#F5F3FF] px-3 py-2 text-[12px] font-semibold text-[#5B21B6]" data-aplicando-tratamento>
      <Loader2 size={14} className="animate-spin" /> Aplicando na arte…
    </p>
  ) : null;
}

/** A paleta da foto (as bolinhas), carregada uma vez por foto. */
export function usePaletaDaFoto(src: string | null) {
  const [paleta, setPaleta] = useState<{ src: string; paleta: PaletaDaFoto } | null>(null);
  useEffect(() => {
    if (!src) return;
    let vivo = true;
    paletaDaFoto(src)
      .then((p) => {
        if (vivo) setPaleta({ src, paleta: p });
      })
      .catch(() => undefined);
    return () => {
      vivo = false;
    };
  }, [src]);
  return src && paleta?.src === src ? paleta.paleta : null;
}

/* A foto de antes em miniatura: clicar pega a cor do ponto (o conta-gotas que funciona em qualquer navegador). */
function FotoParaPegarCor({ src, aoPegar }: { src: string; aoPegar: (cor: string) => void }) {
  const tela = useRef<HTMLCanvasElement | null>(null);
  const [erro, setErro] = useState(false);
  const [sobre, setSobre] = useState<string | null>(null);
  useEffect(() => {
    let vivo = true;
    carregarFoto(src)
      .then((img) => {
        const c = tela.current;
        if (!vivo || !c) return;
        const w = img.naturalWidth || 512;
        const h = img.naturalHeight || 512;
        const k = Math.min(1, 280 / w, 200 / h);
        c.width = Math.max(1, Math.round(w * k));
        c.height = Math.max(1, Math.round(h * k));
        const ctx = c.getContext("2d", { willReadFrequently: true })!;
        ctx.drawImage(img, 0, 0, c.width, c.height);
        ctx.getImageData(0, 0, 1, 1);
      })
      .catch(() => vivo && setErro(true));
    return () => {
      vivo = false;
    };
  }, [src]);
  const corEm = (e: React.PointerEvent<HTMLCanvasElement>) => {
    const c = tela.current;
    if (!c) return null;
    const r = c.getBoundingClientRect();
    const x = Math.floor(((e.clientX - r.left) / r.width) * c.width);
    const y = Math.floor(((e.clientY - r.top) / r.height) * c.height);
    const [pr, pg, pb, pa] = c.getContext("2d", { willReadFrequently: true })!.getImageData(Math.max(0, Math.min(c.width - 1, x)), Math.max(0, Math.min(c.height - 1, y)), 1, 1).data;
    return pa < 20 ? null : hexDoRgb(pr, pg, pb);
  };
  if (erro) return <p className="text-[12px] text-[#6B6584]">Essa imagem vem de outro site e o navegador não deixa ler as cores. Envie a imagem pelo botão Enviar imagem.</p>;
  /* A miniatura na proporção da imagem (sem sobra em volta): o ponto clicado é o ponto da imagem. */
  return (
    <div className="relative flex justify-center rounded-[10px] bg-[repeating-conic-gradient(#EEE_0_25%,#FFF_0_50%)] bg-[length:14px_14px] p-2 ring-1 ring-black/10" data-foto-do-conta-gotas>
      <canvas
        ref={tela}
        className="block h-auto max-w-full cursor-crosshair"
        onPointerMove={(e) => setSobre(corEm(e))}
        onPointerLeave={() => setSobre(null)}
        onPointerDown={(e) => {
          const cor = corEm(e);
          if (cor) aoPegar(cor);
        }}
      />
      {sobre ? (
        <span className="pointer-events-none absolute right-2 top-2 flex items-center gap-1.5 rounded-full bg-white/95 px-2 py-1 text-[11px] font-bold tabular-nums text-[#2B2640] shadow">
          <span className="h-3.5 w-3.5 rounded-full ring-1 ring-black/15" style={{ background: sobre }} />
          {sobre}
        </span>
      ) : null}
    </div>
  );
}

const TROCA_NOVA: Omit<TrocaDeCor, "de" | "para"> = { tolerancia: 30, suavidade: 40, manterLuz: true };

/** "Trocar cores": as cores da imagem em bolinhas e o substituir cor (de → para), com tolerância, suavidade e a luz mantida. */
export function PainelDasCoresDaFoto({
  origem,
  tratamento,
  aoTratar,
  aplicando,
  corInicial,
}: {
  /* A foto de antes (as cores e o conta-gotas leem nela). */
  origem: string;
  tratamento: TratamentoDaFoto | null;
  aoTratar: (campos: { trocas?: TrocaDeCor[] }) => void;
  aplicando: boolean;
  corInicial?: string | null;
}) {
  const paleta = usePaletaDaFoto(origem);
  const trocas = tratamento?.trocas ?? [];
  const [de, setDe] = useState<string | null>(corInicial ?? null);
  const escolhida = de ?? paleta?.cores[0] ?? null;
  const troca = escolhida ? trocas.find((t) => t.de === escolhida) : undefined;
  const mudarTroca = (campos: Partial<TrocaDeCor>) => {
    if (!escolhida) return;
    const nova: TrocaDeCor = { de: escolhida, para: troca?.para ?? escolhida, ...TROCA_NOVA, ...troca, ...campos };
    aoTratar({ trocas: troca ? trocas.map((t) => (t.de === escolhida ? nova : t)) : [...trocas, nova] });
  };
  const paraDe = (cor: string) => trocas.find((t) => t.de === cor)?.para ?? cor;
  return (
    <div data-painel-cores-da-foto>
      <Aplicando aplicando={aplicando} />
      <Secao titulo="Cores desta imagem">
        {paleta ? (
          paleta.cores.length ? (
            <div className="flex flex-wrap gap-2">
              {paleta.cores.map((c) => (
                <button key={c} type="button" title={paraDe(c) === c ? c : `${c} → ${paraDe(c)}`} onClick={() => setDe(c)} className={`h-9 w-9 rounded-full ring-1 ring-black/15 transition hover:scale-110 ${c === escolhida ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`} style={{ background: paraDe(c) }} data-cor-da-foto={c} />
              ))}
            </div>
          ) : (
            <p className="text-[12px] text-[#6B6584]">Não achei cores (a imagem é quase toda transparente).</p>
          )
        ) : (
          <p className="flex items-center gap-2 text-[12px] text-[#6B6584]">
            <Loader2 size={13} className="animate-spin" /> Lendo as cores…
          </p>
        )}
      </Secao>

      <Secao titulo="Substituir cor">
        <p className="mb-2.5 text-[11.5px] leading-snug text-[#6B6584]">Clique na imagem para pegar a cor que vai sair (ou use o conta-gotas da tela) e escolha a cor que entra no lugar.</p>
        <FotoParaPegarCor src={origem} aoPegar={setDe} />
        <div className="mt-3 space-y-2.5">
          <div>
            <span className="mb-1 block text-[11px] font-bold text-[#4B4760]">Cor que sai</span>
            <div className="flex items-center gap-1.5">
              <span className="flex h-9 flex-1 items-center gap-2 rounded-lg border border-[var(--ks-color-border)] bg-white px-2 text-[12px] font-semibold tabular-nums text-[#4B4760]" title={escolhida ?? ""} data-cor-que-sai={escolhida ?? ""}>
                <span className="h-5 w-5 shrink-0 rounded-[5px] ring-1 ring-black/15" style={{ background: escolhida ?? "transparent" }} />
                {escolhida ?? "Clique na imagem"}
              </span>
              <BotaoContaGotas aoPegar={setDe} />
            </div>
          </div>
          {/* A cor que entra na largura toda: a janelinha de cor cabe no painel. */}
          <div data-cor-que-entra>
            <span className="mb-1 block text-[11px] font-bold text-[#4B4760]">Cor que entra</span>
            <Cor valor={troca?.para ?? escolhida ?? "#000000"} aoMudar={(para) => mudarTroca({ para: para.toUpperCase() })} rotulo="Cor que entra" />
          </div>
        </div>
        {troca ? (
          <div className="mt-4 space-y-3.5" data-ajustes-da-troca>
            <Regua rotulo="Tolerância" valor={troca.tolerancia} min={0} max={100} aoMudar={(tolerancia) => mudarTroca({ tolerancia })} formato={(v) => `${Math.round(v)}%`} />
            <Regua rotulo="Suavidade da borda" valor={troca.suavidade} min={0} max={100} aoMudar={(suavidade) => mudarTroca({ suavidade })} formato={(v) => `${Math.round(v)}%`} />
            <Chave rotulo="Manter luz e sombra" ligado={troca.manterLuz} aoMudar={(manterLuz) => mudarTroca({ manterLuz })} />
            <p className="text-[11px] leading-snug text-[#8A84A3]">Tolerância maior pega tons mais distantes da cor. Desligue &quot;Manter luz e sombra&quot; para pintar a cor chapada.</p>
          </div>
        ) : null}
      </Secao>

      {trocas.length ? (
        <Secao
          titulo={`Trocas feitas (${trocas.length})`}
          acao={
            <button type="button" onClick={() => aoTratar({ trocas: [] })} className="flex items-center gap-1 text-[11.5px] font-semibold text-[#7C3AED] hover:underline" data-voltar-cores>
              <RotateCcw size={12} /> Voltar às originais
            </button>
          }
        >
          <ul className="space-y-1.5">
            {trocas.map((t) => (
              <li key={t.de} className={`flex items-center gap-2 rounded-[10px] px-2 py-1.5 ${t.de === escolhida ? "bg-[#F5F3FF]" : "hover:bg-[#F7F6FA]"}`}>
                <button type="button" onClick={() => setDe(t.de)} className="flex flex-1 items-center gap-2 text-left text-[11.5px] font-semibold tabular-nums text-[#4B4760]">
                  <span className="h-6 w-6 rounded-full ring-1 ring-black/15" style={{ background: t.de }} />→<span className="h-6 w-6 rounded-full ring-1 ring-black/15" style={{ background: t.para }} />
                  <span className="truncate">
                    {t.de} → {t.para}
                  </span>
                </button>
                <button type="button" title="Desfazer esta troca" onClick={() => aoTratar({ trocas: trocas.filter((x) => x.de !== t.de) })} className="flex h-7 w-7 items-center justify-center rounded-full text-[#8A84A3] hover:bg-[#FFE4E6] hover:text-[#BE123C]">
                  <Trash2 size={13} />
                </button>
              </li>
            ))}
          </ul>
        </Secao>
      ) : null}
    </div>
  );
}

/* ------------------------------------------------------------ retícula */

/* A amostra de cada estilo: um degradê pequeno passado pela própria retícula (o botão mostra o que sai). */
const amostras = new Map<string, string>();
function amostraDoEstilo(estilo: EstiloDaReticula) {
  const pronta = amostras.get(estilo);
  if (pronta) return pronta;
  const w = 48;
  const h = 32;
  const dados = new Uint8ClampedArray(w * h * 4);
  for (let y = 0; y < h; y += 1)
    for (let x = 0; x < w; x += 1) {
      const v = Math.round(255 * (x / (w - 1)) ** 0.8);
      dados.set([v, v, v, 255], (y * w + x) * 4);
    }
  let url: string;
  if (reticulaEmPixels(estilo)) {
    const p = pontilharPixels(dados, w, h, { ...MEIO_TOM_PADRAO, estilo, tinta: "#2B2640", fundo: null });
    const tela = document.createElement("canvas");
    tela.width = w;
    tela.height = h;
    tela.getContext("2d")!.putImageData(new ImageData(new Uint8ClampedArray(p), w, h), 0, 0);
    url = tela.toDataURL();
  } else url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(reticulaEmSvg(dados, w, h, { ...MEIO_TOM_PADRAO, estilo, celulas: 9, tinta: "#2B2640", minimo: 2 }))}`;
  amostras.set(estilo, url);
  return url;
}

const DPIS = [300, 600, 1000] as const;

/** "Retícula": liga e escolhe o estilo, o tamanho, o ângulo, o tom e as cores; e o que o laser pede. */
export function PainelDaReticula({
  foto,
  tratamento,
  aoTratar,
  aplicando,
  dpi,
}: {
  foto: PecaDeImagem;
  tratamento: TratamentoDaFoto | null;
  aoTratar: (campos: { meioTom?: MeioTom | null }) => void;
  aplicando: boolean;
  /* A arte de impressão (mostra a lineatura em LPI). */
  dpi: number | null;
}) {
  const m = tratamento?.meioTom ?? null;
  const mudar = (campos: Partial<MeioTom>) => aoTratar({ meioTom: { ...(m ?? MEIO_TOM_PADRAO), ...campos } });
  const emPixels = m ? reticulaEmPixels(m.estilo) : false;
  /* Trocar entre vetor e pixels muda o sentido do número (pontos na largura x pixels na largura). */
  const porEstilo = (estilo: EstiloDaReticula) => {
    const atual = m ?? MEIO_TOM_PADRAO;
    const pixels = reticulaEmPixels(estilo);
    const celulas = pixels === reticulaEmPixels(atual.estilo) && m ? atual.celulas : pixels ? 600 : MEIO_TOM_PADRAO.celulas;
    mudar({ estilo, celulas, ...(pixels && atual.cor === "cmyk" ? { cor: "uma" as const } : {}) });
  };
  const lpi = m && !emPixels && dpi ? Math.round(m.celulas / (foto.w / dpi)) : null;
  const pronto = Boolean(m && tratamento?.feito && tratamento.feito === foto.src && !aplicando);
  const [larguraMm, setLarguraMm] = useState(() => Math.round(dpi ? (foto.w / dpi) * 25.4 : 100));
  const [dpiDoLaser, setDpiDoLaser] = useState<(typeof DPIS)[number]>(300);
  const [baixando, setBaixando] = useState(false);
  const proporcao = foto.h / Math.max(1, foto.w);
  const estilos = useMemo(() => ESTILOS_DA_RETICULA.map((e) => ({ ...e, amostra: typeof document === "undefined" ? "" : amostraDoEstilo(e.chave) })), []);
  return (
    <div data-painel-reticula>
      <Aplicando aplicando={aplicando} />
      <Secao
        titulo="Estilo"
        acao={
          m ? (
            <button type="button" onClick={() => aoTratar({ meioTom: null })} className="text-[11.5px] font-semibold text-[#7C3AED] hover:underline" data-tirar-reticula>
              Tirar a retícula
            </button>
          ) : null
        }
      >
        <div className="grid grid-cols-4 gap-2">
          {estilos.map((e) => (
            <button key={e.chave} type="button" title={e.dica} onClick={() => porEstilo(e.chave)} className="group text-center" data-estilo-da-reticula={e.chave} aria-pressed={m?.estilo === e.chave}>
              <span className={`block overflow-hidden rounded-[9px] bg-white ring-1 transition ${m?.estilo === e.chave ? "ring-2 ring-[#7C3AED]" : "ring-black/10 group-hover:ring-[#C4B5FD]"}`}>
                {/* eslint-disable-next-line @next/next/no-img-element -- amostra gerada aqui (data:) */}
                {e.amostra ? <img src={e.amostra} alt="" className="block h-10 w-full object-cover [image-rendering:pixelated]" /> : <span className="block h-10" />}
              </span>
              <span className="mt-1 block text-[10.5px] font-medium leading-tight text-[#4B4760]">{e.nome}</span>
            </button>
          ))}
        </div>
        {!m ? <p className="mt-3 text-[11.5px] leading-snug text-[#6B6584]">Escolha um estilo para ligar a retícula. Os pontos, linhas e formas saem em vetor (nítidos em qualquer tamanho).</p> : null}
      </Secao>

      {m ? (
        <>
          <Secao titulo="Pontos">
            <div className="space-y-3.5">
              {emPixels ? (
                <Regua rotulo="Resolução (pixels na largura)" valor={m.celulas} min={100} max={2400} passo={10} aoMudar={(celulas) => mudar({ celulas })} />
              ) : (
                <>
                  <Regua rotulo="Pontos na largura" valor={m.celulas} min={12} max={300} aoMudar={(celulas) => mudar({ celulas })} formato={(v) => (lpi ? `${Math.round(v)} · ${Math.round(v / (foto.w / dpi!))} LPI` : String(Math.round(v)))} />
                  <Regua rotulo="Ângulo" valor={m.angulo} min={0} max={90} aoMudar={(angulo) => mudar({ angulo })} formato={(v) => `${Math.round(v)}°`} />
                  <Regua rotulo="Ponto mínimo" valor={m.minimo} min={0} max={40} aoMudar={(minimo) => mudar({ minimo })} formato={(v) => `${Math.round(v)}%`} />
                </>
              )}
            </div>
          </Secao>

          <Secao titulo="Tom">
            <div className="space-y-3.5">
              <Regua rotulo="Contraste" valor={m.contraste} min={-100} max={100} aoMudar={(contraste) => mudar({ contraste })} />
              <Regua rotulo="Brilho" valor={m.brilho} min={-100} max={100} aoMudar={(brilho) => mudar({ brilho })} />
              <Chave rotulo="Inverter (pontos no claro)" ligado={m.inverter} aoMudar={(inverter) => mudar({ inverter })} />
              <p className="text-[11px] leading-snug text-[#8A84A3]">Inverter serve para gravar em material escuro (ardósia, couro, madeira escura): o laser marca o claro.</p>
            </div>
          </Secao>

          <Secao titulo="Cores">
            <div className="space-y-3">
              <Opcoes
                valor={m.cor}
                aoMudar={(cor) => mudar({ cor })}
                opcoes={[
                  { valor: "uma", rotulo: "Uma cor" },
                  { valor: "original", rotulo: "Da foto" },
                  ...(emPixels ? [] : [{ valor: "cmyk" as const, rotulo: "CMYK", titulo: "As quatro tintas da gráfica, cada uma num ângulo (efeito de revista)" }]),
                ]}
              />
              {m.cor === "uma" ? <Cor valor={m.tinta} aoMudar={(tinta) => mudar({ tinta: tinta.toUpperCase() })} rotulo="Cor dos pontos" /> : null}
              <div>
                <span className="mb-1 block text-[11px] font-bold text-[#4B4760]">Papel (atrás dos pontos)</span>
                <div className="flex items-center gap-2">
                  <div className="flex-1">
                    <Opcoes
                      valor={m.fundo === null ? "nenhum" : m.fundo === "#FFFFFF" ? "branco" : "cor"}
                      aoMudar={(v) => mudar({ fundo: v === "nenhum" ? null : v === "branco" ? "#FFFFFF" : m.fundo && m.fundo !== "#FFFFFF" ? m.fundo : "#F5E6C8" })}
                      opcoes={[
                        { valor: "nenhum", rotulo: "Transparente" },
                        { valor: "branco", rotulo: "Branco" },
                        { valor: "cor", rotulo: "Cor" },
                      ]}
                    />
                  </div>
                  {m.fundo && m.fundo !== "#FFFFFF" ? <Cor valor={m.fundo} aoMudar={(fundo) => mudar({ fundo: fundo.toUpperCase() })} rotulo="Cor do papel" compacta /> : null}
                </div>
              </div>
            </div>
          </Secao>

          <Secao titulo="Gravação a laser">
            <button type="button" onClick={() => mudar({ ...MEIO_TOM_LASER })} className="mb-3 flex h-10 w-full items-center justify-center gap-2 rounded-[10px] bg-[#1F1B2E] text-[12.5px] font-bold text-white hover:bg-black" data-pronto-para-laser>
              <Zap size={15} /> Deixar pronto para laser
            </button>
            <p className="mb-3 text-[11px] leading-snug text-[#8A84A3]">Preto puro, sem cinza, com papel branco. Para foto, o Pontilhado costuma gravar melhor; para logo e arte, os Pontos.</p>
            <div className="grid grid-cols-2 gap-2">
              <label className="flex h-9 items-center gap-1.5 rounded-lg border border-[var(--ks-color-border)] bg-white px-2 focus-within:border-[#A78BFA]">
                <span className="text-[10.5px] font-bold text-[#8A84A3]">Largura</span>
                <input
                  inputMode="numeric"
                  value={String(larguraMm)}
                  onChange={(e) => {
                    const v = Number(e.target.value.replace(/\D/g, "").slice(0, 4));
                    setLarguraMm(Number.isFinite(v) ? v : 0);
                  }}
                  className="w-full min-w-0 bg-transparent text-right text-[12.5px] font-semibold tabular-nums outline-none"
                  aria-label="Largura em milímetros"
                  data-largura-do-laser
                />
                <span className="text-[10.5px] text-[#8A84A3]">mm</span>
              </label>
              <Opcoes valor={String(dpiDoLaser) as `${(typeof DPIS)[number]}`} aoMudar={(v) => setDpiDoLaser(Number(v) as (typeof DPIS)[number])} opcoes={DPIS.map((d) => ({ valor: String(d) as `${(typeof DPIS)[number]}`, rotulo: `${d}` }))} />
            </div>
            <p className="mt-1.5 text-[10.5px] text-[#8A84A3]">
              {larguraMm || 0} × {Math.round((larguraMm || 0) * proporcao)} mm a {dpiDoLaser} DPI
            </p>
            <div className="mt-3 grid grid-cols-1 gap-2">
              <button
                type="button"
                disabled={!pronto || baixando || larguraMm < 5}
                onClick={async () => {
                  setBaixando(true);
                  try {
                    const px = Math.min(12000, Math.round((larguraMm / 25.4) * dpiDoLaser));
                    const arquivo = await pngParaLaser(foto.src, px, Math.min(12000, Math.round(px * proporcao)), dpiDoLaser, emPixels);
                    baixarArquivo(arquivo, `retícula-${larguraMm}mm-${dpiDoLaser}dpi.png`);
                  } finally {
                    setBaixando(false);
                  }
                }}
                className="flex h-10 items-center justify-center gap-2 rounded-[10px] bg-[#F1EEFB] text-[12.5px] font-bold text-[#5B21B6] hover:bg-[#E7E1F8] disabled:opacity-50"
                data-baixar-png-laser
              >
                {baixando ? <Loader2 size={15} className="animate-spin" /> : <Download size={15} />} PNG preto e branco (com DPI)
              </button>
              {!emPixels ? (
                <button
                  type="button"
                  disabled={!pronto}
                  onClick={async () => {
                    const arquivo = await (await fetch(foto.src)).blob();
                    baixarArquivo(new Blob([arquivo], { type: "image/svg+xml" }), "retícula.svg");
                  }}
                  className="flex h-10 items-center justify-center gap-2 rounded-[10px] bg-[#F1EEFB] text-[12.5px] font-bold text-[#5B21B6] hover:bg-[#E7E1F8] disabled:opacity-50"
                  data-baixar-svg-laser
                >
                  <Download size={15} /> SVG em vetor (LightBurn, Corel)
                </button>
              ) : null}
            </div>
            {!pronto ? <p className="mt-2 text-[10.5px] text-[#8A84A3]">Os arquivos ficam prontos assim que a retícula termina de aplicar.</p> : null}
          </Secao>
        </>
      ) : null}
    </div>
  );
}
