"use client";

import { useEffect, useMemo, useState } from "react";
import { Loader2 } from "lucide-react";
import { fonteDaChave, fontesDaMarca, pesoQueExiste, type PecaDeTexto } from "@/lib/banner-editor/documento";
import { FONTES_COM_GLIFOS } from "@/lib/banner-editor/fontes-com-glifos";
import { RECURSOS_DA_LETRA, cssDosRecursos, glifosEmGrupos, type RecursosDaLetra } from "@/lib/banner-editor/glifos";
import { inserirNoTexto } from "./texto-rico";

/*
 * O painel "Glifos" do texto: as letras com floreio (as "perninhas" das
 * fontes manuscritas, como no Studio e no Illustrator). Cada letra mostra os
 * outros desenhos que a fonte tem para ela; o clique troca a letra escolhida
 * no texto (ou entra no cursor, ou no fim). A letra nova é um trecho com o
 * recurso da fonte ('salt' 2, 'ss01' 1), então sai igual no download.
 * Embaixo, os enfeites soltos da fonte, os recursos para a palavra toda e os
 * caracteres especiais.
 */

type Leitura = { caracteres: number[]; recursos: string[]; alternativas: Record<string, string[]> };
const lidas = new Map<string, Promise<Leitura>>();

function lerGlifos(endereco: string) {
  let pedido = lidas.get(endereco);
  if (!pedido) {
    pedido = fetch(endereco).then(async (r) => {
      const dados = (await r.json().catch(() => ({}))) as Partial<Leitura> & { error?: string };
      if (!r.ok) throw new Error(dados.error || "Não deu para ler a fonte agora.");
      return { caracteres: dados.caracteres || [], recursos: dados.recursos || [], alternativas: dados.alternativas || {} };
    });
    pedido.catch(() => lidas.delete(endereco));
    lidas.set(endereco, pedido);
  }
  return pedido;
}

/* Os recursos que o próprio navegador já liga (desligar é a escolha). */
const LIGADOS_SOZINHOS = new Set(RECURSOS_DA_LETRA.filter((r) => r.padrao).map((r) => r.tag));
const escapar = (t: string) => t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

export function PainelDeGlifos({ p, mudar }: { p: PecaDeTexto; mudar: (c: Partial<PecaDeTexto>) => void }) {
  const fonte = fonteDaChave(p.fonte);
  const daMarca = fonte.propria ? fontesDaMarca().find((f) => f.chave === fonte.chave) : null;
  const endereco = daMarca ? `/api/artes/glifos?url=${encodeURIComponent(daMarca.url)}` : `/api/artes/glifos?fonte=${encodeURIComponent(fonte.chave)}&peso=${pesoQueExiste(fonte.chave, p.peso)}`;
  const [leitura, setLeitura] = useState<{ endereco: string; dados?: Leitura; erro?: string } | null>(null);
  const [mais, setMais] = useState<Record<string, boolean>>({});
  useEffect(() => {
    let vivo = true;
    lerGlifos(endereco)
      .then((dados) => vivo && setLeitura({ endereco, dados }))
      .catch((e) => vivo && setLeitura({ endereco, erro: e instanceof Error ? e.message : "Não deu para ler a fonte agora." }));
    return () => {
      vivo = false;
    };
  }, [endereco]);
  const atual = leitura?.endereco === endereco ? leitura : null;
  const dados = atual?.dados;
  const grupos = useMemo(() => (dados ? glifosEmGrupos(dados.caracteres) : []), [dados]);
  /* As letras (e números) com outro desenho, na ordem do alfabeto: maiúsculas, minúsculas, o resto. */
  const comFloreio = useMemo(() => {
    const lista = Object.entries(dados?.alternativas || {}).map(([c, variantes]) => ({ letra: String.fromCodePoint(Number(c)), variantes }));
    const ordem = (l: string) => (/[A-Z]/.test(l) ? 0 : /[a-z]/.test(l) ? 1 : /[0-9]/.test(l) ? 2 : 3);
    /* As letras do português (sem os acentos de outras línguas, que só enchem a lista). */
    return lista.filter((x) => /^[A-Za-z0-9ÁÉÍÓÚÂÊÔÃÕÀÇáéíóúâêôãõàç]$/.test(x.letra)).sort((a, b) => ordem(a.letra) - ordem(b.letra) || a.letra.localeCompare(b.letra));
  }, [dados]);
  /* Os enfeites soltos (a área particular da fonte), sem o logo da Apple que algumas trazem em F8FF. */
  const enfeites = grupos.find((g) => g.nome === "Extras da fonte")?.lista.filter((c) => c !== 0xf8ff) ?? [];
  const outros = grupos.filter((g) => g.nome !== "Extras da fonte");
  const recursos = RECURSOS_DA_LETRA.filter((r) => dados?.recursos.includes(r.tag));
  const ligado = (tag: string) => (p.recursos?.[tag] ?? (LIGADOS_SOZINHOS.has(tag) ? 1 : 0)) > 0;
  const alternar = (tag: string) => {
    const proximos: RecursosDaLetra = { ...(p.recursos || {}) };
    const agora = ligado(tag);
    /* O que o navegador já liga sozinho só entra na peça quando é desligado (e vice-versa). */
    if (LIGADOS_SOZINHOS.has(tag) ? !agora : agora) delete proximos[tag];
    else proximos[tag] = agora ? 0 : 1;
    mudar({ recursos: Object.keys(proximos).length ? proximos : null });
  };
  /* Põe no texto: escrevendo, no cursor (ou no lugar da letra escolhida); senão, no fim. */
  const por = (letra: string, recurso?: string) => {
    const html = recurso ? `<span style="font-feature-settings:${recurso}">${escapar(letra)}</span>` : undefined;
    if (inserirNoTexto(letra, html)) return;
    const atualEmHtml = p.html || escapar(p.texto).replace(/\n/g, "<br>");
    mudar(html ? { texto: `${p.texto}${letra}`, html: `${atualEmHtml}${html}` } : { texto: `${p.texto}${letra}`, ...(p.html ? { html: `${p.html}${escapar(letra)}` } : {}) });
  };
  const estiloDaLetra = { fontFamily: `'${fonte.familia}', sans-serif`, fontWeight: pesoQueExiste(fonte.chave, p.peso), fontStyle: p.italico ? "italic" : undefined } as const;
  const nomeDaFonte = (fonte.nome || fonte.familia).replace(" (com glifos)", "");

  if (!atual) {
    return (
      <div className="flex items-center gap-2 text-[12.5px] text-[#8A849E]">
        <Loader2 size={15} className="animate-spin" /> Lendo os glifos da {nomeDaFonte}…
      </div>
    );
  }
  if (atual.erro) return <p className="rounded-[12px] bg-[#F6F4FB] p-3 text-[12.5px] leading-snug text-[#6B6584]">{atual.erro}</p>;
  return (
    <div data-painel-de-glifos>
      {comFloreio.length ? (
        <section className="mb-6" data-letras-com-floreio>
          <h3 className="mb-1 text-[13px] font-bold text-[#1F1B2E]">Letras com floreio</h3>
          <p className="mb-3 text-[11.5px] leading-snug text-[#8A849E]">Dois cliques no texto, escolha a letra (arraste por cima dela) e clique na versão com perninha: ela troca. Com o cursor parado, entra ali.</p>
          <div className="space-y-1.5">
            {comFloreio.map(({ letra, variantes }) => (
              <div key={letra} className="flex items-stretch gap-1" data-letra-com-floreio={letra}>
                <span className="flex w-7 shrink-0 items-center justify-center text-[12px] font-bold text-[#8A849E]">{letra}</span>
                <div className="flex min-w-0 flex-1 flex-wrap gap-1">
                  {variantes.map((recurso) => (
                    <button
                      key={recurso}
                      type="button"
                      onMouseDown={(e) => e.preventDefault()}
                      onClick={() => por(letra, recurso)}
                      title={`${letra} com floreio`}
                      className="flex h-[52px] min-w-[52px] items-center justify-center rounded-[10px] bg-[#F6F4FB] px-2 text-[30px] leading-none text-[#1F1B2E] ring-1 ring-black/5 transition hover:bg-[#EDE7FF] hover:ring-[#8B5CF6]"
                      style={{ ...estiloDaLetra, fontFeatureSettings: recurso }}
                      data-floreio={recurso}
                    >
                      {letra}
                    </button>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </section>
      ) : (
        <section className="mb-6 rounded-[12px] bg-[#F6F4FB] p-3" data-sem-floreio>
          <p className="text-[12.5px] font-bold text-[#1F1B2E]">A {nomeDaFonte} não tem letras com floreio</p>
          <p className="mt-0.5 text-[11.5px] leading-snug text-[#6B6584]">Troque por uma fonte com glifos (ou envie a sua no Kit da marca, em TTF ou OTF):</p>
          <div className="mt-2 grid grid-cols-2 gap-1.5">
            {FONTES_COM_GLIFOS.slice(0, 8).map((f) => (
              <button key={f.chave} type="button" onClick={() => mudar({ fonte: f.chave })} className="truncate rounded-[9px] bg-white px-2 py-2 text-left text-[18px] leading-none text-[#1F1B2E] ring-1 ring-black/5 transition hover:ring-[#8B5CF6]" style={{ fontFamily: `'${f.chave}', sans-serif` }} data-trocar-para={f.chave}>
                {f.nome.replace(" (com glifos)", "")}
              </button>
            ))}
          </div>
        </section>
      )}
      {enfeites.length ? (
        <section className="mb-6" data-enfeites-da-fonte>
          <h3 className="mb-2.5 flex items-baseline justify-between text-[13px] font-bold text-[#1F1B2E]">
            Enfeites da fonte <span className="text-[11px] font-medium text-[#8A849E]">{enfeites.length}</span>
          </h3>
          <div className="grid grid-cols-3 gap-1">
            {enfeites.slice(0, mais.enfeites ? enfeites.length : 30).map((c) => (
              <button key={c} type="button" onMouseDown={(e) => e.preventDefault()} onClick={() => por(String.fromCodePoint(c))} className="flex h-16 items-center justify-center overflow-hidden rounded-[10px] bg-[#F6F4FB] text-[34px] leading-none text-[#1F1B2E] ring-1 ring-black/5 transition hover:bg-[#EDE7FF] hover:ring-[#8B5CF6]" style={estiloDaLetra} data-glifo={c}>
                {String.fromCodePoint(c)}
              </button>
            ))}
          </div>
          {enfeites.length > 30 && !mais.enfeites ? (
            <button type="button" onClick={() => setMais((m) => ({ ...m, enfeites: true }))} className="mt-2 h-8 w-full rounded-[9px] bg-[#F3F1F9] text-[12px] font-semibold text-[#4B4760] hover:bg-[#EAE5F7]">
              Mais {enfeites.length - 30}
            </button>
          ) : null}
        </section>
      ) : null}
      {recursos.length ? (
        <section className="mb-6">
          <h3 className="mb-2.5 text-[13px] font-bold text-[#1F1B2E]">Na palavra toda</h3>
          <div className="space-y-1">
            {recursos.map((r) => (
              <label key={r.tag} className="flex cursor-pointer items-center justify-between gap-3 rounded-[10px] px-2.5 py-2 transition hover:bg-[#F6F4FB]" data-recurso-da-letra={r.tag}>
                <span className="min-w-0">
                  <span className="block text-[12.5px] font-bold text-[#1F1B2E]">{r.nome}</span>
                  <span className="block text-[11px] leading-snug text-[#8A849E]">{r.detalhe}</span>
                </span>
                <input type="checkbox" checked={ligado(r.tag)} onChange={() => alternar(r.tag)} className="h-4 w-4 shrink-0 accent-[#7C3AED]" />
              </label>
            ))}
          </div>
          {cssDosRecursos(p.recursos) ? (
            <p className="mt-2 truncate rounded-[8px] bg-[#F6F4FB] px-2.5 py-1.5 text-[#4B4760]" style={{ ...estiloDaLetra, fontFeatureSettings: cssDosRecursos(p.recursos), fontSize: 22 }}>
              {p.texto.split("\n")[0].slice(0, 40) || "Aa Bb Cc"}
            </p>
          ) : null}
        </section>
      ) : null}
      {outros.map((g) => {
        const quantos = mais[g.nome] ? g.lista.length : 60;
        return (
          <section key={g.nome} className="mb-6">
            <h3 className="mb-2.5 flex items-baseline justify-between text-[13px] font-bold text-[#1F1B2E]">
              {g.nome} <span className="text-[11px] font-medium text-[#8A849E]">{g.lista.length}</span>
            </h3>
            <div className="grid grid-cols-6 gap-1">
              {g.lista.slice(0, quantos).map((c) => (
                <button
                  key={c}
                  type="button"
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => por(String.fromCodePoint(c))}
                  title={`U+${c.toString(16).toUpperCase().padStart(4, "0")}`}
                  className="flex aspect-square items-center justify-center rounded-[9px] bg-[#F6F4FB] text-[19px] leading-none text-[#1F1B2E] ring-1 ring-black/5 transition hover:bg-[#EDE7FF] hover:ring-[#8B5CF6]"
                  style={estiloDaLetra}
                  data-glifo={c}
                >
                  {String.fromCodePoint(c)}
                </button>
              ))}
            </div>
            {g.lista.length > quantos ? (
              <button type="button" onClick={() => setMais((m) => ({ ...m, [g.nome]: true }))} className="mt-2 h-8 w-full rounded-[9px] bg-[#F3F1F9] text-[12px] font-semibold text-[#4B4760] hover:bg-[#EAE5F7]">
                Mais {g.lista.length - quantos}
              </button>
            ) : null}
          </section>
        );
      })}
    </div>
  );
}
