"use client";

import { createElement, useEffect, useMemo, useRef, useState } from "react";
import {
  Boxes,
  Brush,
  Check,
  ChevronDown,
  Crop,
  Droplet,
  FileText,
  Gift,
  Hash,
  Image as ImageIcon,
  Layers,
  Package,
  PenLine,
  Pencil,
  PlusCircle,
  Ruler,
  Search,
  Shapes,
  Sparkles,
  Truck,
  X,
} from "lucide-react";
import {
  corDoRotulo,
  corPrecisaDeAnel,
  semAcento,
  variacoesDaVitrine,
  type AtributoDaVitrine,
  type OpcaoDaVitrine,
} from "@/lib/variacoes-da-loja";
import { sufixoDoAdicional } from "@/lib/adicionais";

/*
  As variações, do jeito que o cliente entende.

  Cor vira bolinha, estampa vira miniatura, tamanho vira chip, um atributo com
  trinta opções vira lista com busca ou um menu que abre. Quando o produto tem
  três ou mais escolhas, o que já foi resolvido recolhe numa linha e o que
  falta continua à vista: é o que encurta o caminho até o Comprar.

  Escolha múltipla é a exceção da regra: ela **não** recolhe no primeiro
  clique — quem marca "Refil extra" costuma querer marcar "Cordão" também.
  Só recolhe quando a pessoa vai mexer em outra coisa.

  O que sai daqui é exatamente o que saía antes — o mesmo `handleOptionSelect`,
  com nome do atributo e rótulo da opção em texto. O pacote que chega ao
  pedido não mudou uma vírgula, e não podia: vinte e uma funções do banco
  leem esse formato.
*/

type Props = {
  atributos: Parameters<typeof variacoesDaVitrine>[0];
  selecionadas: Record<string, string[]>;
  aoEscolher: (nomeDoAtributo: string, rotulo: string, multipla: boolean, obrigatoria: boolean) => void;
  corDaLoja: string;
  corDoTextoNaLoja: string;
  corDaLojaNoBranco: string;
  formatarDinheiro: (valor: number) => string;
  imagens?: { id: string; image_url: string }[] | null;
  /* Qual atributo controla o estoque — só nele a opção pode aparecer esgotada. */
  atributoDeEstoque?: string | null;
  camposDaOpcao?: (nomeDoAtributo: string) => React.ReactNode;
  /* A unidade de venda do produto: decide se o adicional é "/m²", "por peça" ou "no pedido". */
  unidadeDeVenda?: string | null;
};

/* A partir de três escolhas a página vira rolagem: aí vale recolher. */
const A_PARTIR_DE_QUANTAS_RECOLHE = 3;

function esgotada(opcao: OpcaoDaVitrine, controlaEstoque: boolean) {
  return controlaEstoque && typeof opcao.estoque === "number" && opcao.estoque <= 0;
}

/* ---------------------------------------------------------------- *
 *  O ícone que combina com o nome
 * ---------------------------------------------------------------- */

/*
  A vitrine lê o nome do atributo e escolhe o desenho: "Tamanho" ganha a
  régua, "Embalagem" o presente, "Prazo" o caminhão. É o que faz a lista de
  escolhas recolhidas ser varrida de olho em vez de lida palavra por palavra.

  A ordem importa: o primeiro que casar ganha, então o mais específico vem
  antes ("cor da tinta" antes de "cor").
*/
const ICONES: Array<[RegExp, typeof Ruler]> = [
  [/\btinta|caneta\b/, Droplet],
  [/\bcor(es)?\b|\btom|tonalidade/, Brush],
  [/tamanho|medida|dimens|altura|largura|comprimento/, Ruler],
  [/espessura|gramatura|grossura|ponta/, PenLine],
  [/acabamento|verniz|laminac|brilho|fosco/, Sparkles],
  [/papel|material|tecido|substrato/, Layers],
  [/formato|corte|recorte|refile/, Crop],
  [/personaliz|gravac|impress|estampa\b/, Pencil],
  [/embalagem|caixa|sacola|pacote/, Gift],
  [/quantidade|volume|lote/, Hash],
  [/prazo|entrega|frete|envio/, Truck],
  [/modelo|estilo|linha\b/, Shapes],
  [/adicional|extra|acess|complement|servic/, PlusCircle],
  [/arte|arquivo|layout|design/, FileText],
  [/imagem|foto|face|lado/, ImageIcon],
  [/kit|combo|conjunto/, Boxes],
];

function iconeDoAtributo(nome: string) {
  const limpo = semAcento(nome);
  for (const [padrao, Icone] of ICONES) {
    if (padrao.test(limpo)) return Icone;
  }
  return Package;
}

/*
  O ícone entra por aqui em vez de virar uma variável montada no meio do
  render — para o React (e para o lint) é sempre o mesmo componente, e o que
  muda é só o desenho que ele devolve.
*/
function IconeDaVariacao({ nome, size = 13, className }: { nome: string; size?: number; className?: string }) {
  return createElement(iconeDoAtributo(nome), { size, className, "aria-hidden": true });
}

/* ---------------------------------------------------------------- *
 *  Bolinha de cor
 * ---------------------------------------------------------------- */

function Bolinha({
  opcao,
  marcada,
  fora,
  corDaLoja,
}: {
  opcao: OpcaoDaVitrine;
  marcada: boolean;
  fora: boolean;
  corDaLoja: string;
}) {
  const cor = opcao.swatch ? { tipo: "chapada" as const, fundo: opcao.swatch } : corDoRotulo(opcao.label);
  const anel = cor ? corPrecisaDeAnel(cor) : true;

  /* Cada tipo de cor tem seu fundo: chapada é tinta, metálico brilha,
     transparente mostra o xadrez, sortido é o arco-íris. */
  const fundo = !cor
    ? { background: "#e6e8ec" }
    : cor.tipo === "brilho" && cor.brilho
      ? { background: `linear-gradient(135deg, ${cor.brilho[0]} 0%, ${cor.fundo} 45%, ${cor.brilho[1]} 100%)` }
      : cor.tipo === "arcoiris"
        ? { background: "conic-gradient(from 210deg, #ef4444, #f59e0b, #22c55e, #06b6d4, #6366f1, #ec4899, #ef4444)" }
        : cor.tipo === "xadrez"
          ? {
              backgroundColor: "#ffffff",
              backgroundImage:
                "linear-gradient(45deg, #d8dbe0 25%, transparent 25%, transparent 75%, #d8dbe0 75%), linear-gradient(45deg, #d8dbe0 25%, transparent 25%, transparent 75%, #d8dbe0 75%)",
              backgroundSize: "8px 8px",
              backgroundPosition: "0 0, 4px 4px",
            }
          : { background: cor.fundo };

  return (
    <span
      aria-hidden="true"
      className={`block h-8 w-8 rounded-full transition-transform ${fora ? "opacity-30" : ""} ${marcada ? "scale-105" : ""}`}
      style={{
        ...fundo,
        boxShadow: marcada
          ? `0 0 0 2px #fff, 0 0 0 3.5px ${corDaLoja}`
          : anel
            ? "inset 0 0 0 1px rgba(24,24,27,0.16)"
            : "inset 0 0 0 1px rgba(255,255,255,0.25)",
      }}
    />
  );
}

/* ---------------------------------------------------------------- *
 *  Menu da loja — nunca o do navegador
 * ---------------------------------------------------------------- */

function Menu({
  atributo,
  escolhidas,
  aoMarcar,
  corDaLoja,
  corDaLojaNoBranco,
  formatarDinheiro,
  controlaEstoque,
  unidadeDeVenda,
}: {
  atributo: AtributoDaVitrine;
  escolhidas: string[];
  aoMarcar: (opcao: OpcaoDaVitrine) => void;
  corDaLoja: string;
  corDaLojaNoBranco: string;
  formatarDinheiro: (valor: number) => string;
  controlaEstoque: boolean;
  unidadeDeVenda?: string | null;
}) {
  const [aberto, setAberto] = useState(false);
  const caixa = useRef<HTMLDivElement | null>(null);

  /* O menu do navegador não aceita a cor da loja, não cabe um "+ R$ 2,00"
     alinhado e no celular vira a roda do sistema. Este é da casa — e por
     isso precisa fechar sozinho no clique fora e no Esc. */
  useEffect(() => {
    if (!aberto) return;
    const foraDaCaixa = (evento: MouseEvent) => {
      if (caixa.current && !caixa.current.contains(evento.target as Node)) setAberto(false);
    };
    const noEsc = (evento: KeyboardEvent) => {
      if (evento.key === "Escape") setAberto(false);
    };
    document.addEventListener("mousedown", foraDaCaixa);
    document.addEventListener("keydown", noEsc);
    return () => {
      document.removeEventListener("mousedown", foraDaCaixa);
      document.removeEventListener("keydown", noEsc);
    };
  }, [aberto]);

  const resumo = escolhidas.length ? escolhidas.join(", ") : "Selecione";

  return (
    <div ref={caixa} className="relative">
      <button
        type="button"
        onClick={() => setAberto((antes) => !antes)}
        aria-expanded={aberto}
        aria-haspopup="listbox"
        className="flex w-full items-center justify-between gap-2 rounded-xl border bg-white px-3.5 py-2.5 text-left text-[13.5px] transition-colors hover:border-zinc-300"
        style={{ borderColor: aberto ? corDaLoja : "#e4e4e7" }}
      >
        <span className={`min-w-0 flex-1 truncate ${escolhidas.length ? "font-medium text-zinc-900" : "text-zinc-400"}`}>
          {resumo}
        </span>
        <ChevronDown size={15} className={`shrink-0 text-zinc-400 transition-transform ${aberto ? "rotate-180" : ""}`} />
      </button>

      {aberto ? (
        <div
          role="listbox"
          className="absolute left-0 right-0 top-[calc(100%+6px)] z-30 max-h-[260px] overflow-y-auto rounded-xl border border-zinc-200 bg-white py-1 shadow-[0_16px_40px_rgba(24,24,27,0.14)]"
        >
          {atributo.opcoes.map((opcao) => {
            const marcada = escolhidas.includes(opcao.label);
            const fora = esgotada(opcao, controlaEstoque);
            return (
              <button
                key={opcao.label}
                type="button"
                role="option"
                aria-selected={marcada}
                disabled={fora}
                onClick={() => {
                  if (fora) return;
                  aoMarcar(opcao);
                  if (!atributo.multipla) setAberto(false);
                }}
                className={`flex w-full items-center gap-2.5 px-3.5 py-2.5 text-left text-[13.5px] ${
                  fora ? "cursor-not-allowed text-zinc-300" : "hover:bg-zinc-50"
                }`}
              >
                <span className={`min-w-0 flex-1 truncate ${marcada ? "font-semibold text-zinc-900" : "text-zinc-700"} ${fora ? "line-through" : ""}`}>
                  {opcao.label}
                </span>
                {opcao.preco > 0 ? (
                  <span className="shrink-0 text-[12px] text-zinc-500">
                    + {formatarDinheiro(opcao.preco)}
                    {sufixoDoAdicional({ price: opcao.preco, price_unit: opcao.unidadeDoPreco, price_applies_per_quantity: opcao.precoPorPeca }, unidadeDeVenda)}
                  </span>
                ) : null}
                {fora ? <span className="shrink-0 text-[11px]">esgotado</span> : null}
                {marcada ? <Check size={14} className="shrink-0" style={{ color: corDaLojaNoBranco }} /> : null}
              </button>
            );
          })}
        </div>
      ) : null}
    </div>
  );
}

/* ---------------------------------------------------------------- *
 *  Um atributo
 * ---------------------------------------------------------------- */

function Atributo({
  atributo,
  escolhidas,
  aoEscolher,
  corDaLoja,
  corDoTextoNaLoja,
  corDaLojaNoBranco,
  formatarDinheiro,
  imagens,
  controlaEstoque,
  campos,
  unidadeDeVenda,
}: {
  atributo: AtributoDaVitrine;
  escolhidas: string[];
  aoEscolher: Props["aoEscolher"];
  corDaLoja: string;
  corDoTextoNaLoja: string;
  corDaLojaNoBranco: string;
  formatarDinheiro: (valor: number) => string;
  imagens: Map<string, string>;
  controlaEstoque: boolean;
  campos: React.ReactNode;
  unidadeDeVenda?: string | null;
}) {
  const [busca, setBusca] = useState("");

  const visiveis = useMemo(() => {
    if (atributo.estilo !== "lista" || !busca.trim()) return atributo.opcoes;
    const termo = semAcento(busca);
    return atributo.opcoes.filter((opcao) => semAcento(opcao.label).includes(termo));
  }, [atributo.estilo, atributo.opcoes, busca]);

  const marcar = (opcao: OpcaoDaVitrine) =>
    aoEscolher(atributo.nomeOriginal, opcao.label, atributo.multipla, opcao.obrigatoria);

  /* "+ R$ 12,00/m²", "+ R$ 250,00 por peça", "+ R$ 80,00 no pedido": o cliente
     vê em que o adicional se multiplica antes de marcar. */
  const precoDaOpcao = (opcao: OpcaoDaVitrine) =>
    opcao.preco > 0
      ? `+ ${formatarDinheiro(opcao.preco)}${sufixoDoAdicional({ price: opcao.preco, price_unit: opcao.unidadeDoPreco, price_applies_per_quantity: opcao.precoPorPeca }, unidadeDeVenda)}`
      : null;

  const cabecalho = (
    <div className="flex flex-wrap items-center gap-x-2 gap-y-1">
      <IconeDaVariacao nome={atributo.nomeOriginal} className="shrink-0 text-zinc-400" />
      <span className="text-[11px] font-semibold uppercase tracking-[0.08em] text-zinc-500">{atributo.nome}</span>
      {/* Na bolinha o nome da cor escolhida vive aqui: a bolinha mostra a cor,
          o cabeçalho diz como ela se chama. */}
      {escolhidas.length > 0 ? (
        <span className="text-[13px] font-semibold text-zinc-800">{escolhidas.join(", ")}</span>
      ) : atributo.obrigatorio ? (
        <span className="text-[11px] font-medium" style={{ color: corDaLojaNoBranco }}>
          escolha uma
        </span>
      ) : null}
      {atributo.multipla ? (
        <span className="text-[10.5px] font-medium text-zinc-400">pode marcar mais de uma</span>
      ) : null}
    </div>
  );

  /* ---- cores: só a bolinha; o nome está no cabeçalho e no toque ---- */
  if (atributo.estilo === "cores") {
    return (
      <div className="space-y-2">
        {cabecalho}
        <div className="flex flex-wrap gap-x-1 gap-y-2">
          {atributo.opcoes.map((opcao) => {
            const marcada = escolhidas.includes(opcao.label);
            const fora = esgotada(opcao, controlaEstoque);
            const preco = precoDaOpcao(opcao);
            return (
              <button
                key={opcao.label}
                type="button"
                onClick={() => !fora && marcar(opcao)}
                disabled={fora}
                aria-pressed={marcada}
                aria-label={`${opcao.label}${preco ? ` (${preco})` : ""}${fora ? " — esgotado" : ""}`}
                title={fora ? `${opcao.label} — esgotado` : preco ? `${opcao.label} · ${preco}` : opcao.label}
                className={`flex w-[52px] flex-col items-center gap-1.5 ${fora ? "cursor-not-allowed" : ""}`}
              >
                <Bolinha opcao={opcao} marcada={marcada} fora={fora} corDaLoja={corDaLoja} />
                {/* O acréscimo fica embaixo da bolinha: o nome da cor a própria
                    bolinha mostra, mas quanto ela custa a mais, não. */}
                {preco ? (
                  <span className="text-[10px] font-semibold leading-none text-zinc-500">{preco}</span>
                ) : null}
              </button>
            );
          })}
        </div>
        {campos}
      </div>
    );
  }

  /* ---- miniaturas: a foto diz mais que o nome ---- */
  if (atributo.estilo === "miniaturas") {
    return (
      <div className="space-y-2">
        {cabecalho}
        <div className="flex flex-wrap gap-2">
          {atributo.opcoes.map((opcao) => {
            const marcada = escolhidas.includes(opcao.label);
            const fora = esgotada(opcao, controlaEstoque);
            const foto = opcao.imageId ? imagens.get(opcao.imageId) : null;
            const preco = precoDaOpcao(opcao);
            return (
              <button
                key={opcao.label}
                type="button"
                onClick={() => !fora && marcar(opcao)}
                disabled={fora}
                aria-pressed={marcada}
                aria-label={`${opcao.label}${preco ? ` (${preco})` : ""}`}
                title={preco ? `${opcao.label} · ${preco}` : opcao.label}
                className={`relative block h-14 w-14 overflow-hidden rounded-xl bg-zinc-100 ${fora ? "cursor-not-allowed opacity-40" : ""}`}
                style={{
                  boxShadow: marcada ? `0 0 0 2px #fff, 0 0 0 3.5px ${corDaLoja}` : "inset 0 0 0 1px rgba(24,24,27,0.12)",
                }}
              >
                {foto ? (
                  // eslint-disable-next-line @next/next/no-img-element
                  <img src={foto} alt={opcao.label} className="h-full w-full object-cover" loading="lazy" />
                ) : (
                  <span className="flex h-full w-full items-center justify-center text-[11px] font-semibold text-zinc-400">
                    {opcao.label.slice(0, 2).toUpperCase()}
                  </span>
                )}
                {preco ? (
                  <span
                    aria-hidden="true"
                    className="absolute right-1 top-1 h-[7px] w-[7px] rounded-full border border-white"
                    style={{ backgroundColor: corDaLoja }}
                  />
                ) : null}
              </button>
            );
          })}
        </div>
        {campos}
      </div>
    );
  }

  /* ---- menu da loja: uma linha só, para quando o espaço é curto ---- */
  if (atributo.estilo === "dropdown") {
    return (
      <div className="space-y-2">
        {cabecalho}
        <Menu
          atributo={atributo}
          escolhidas={escolhidas}
          aoMarcar={marcar}
          corDaLoja={corDaLoja}
          corDaLojaNoBranco={corDaLojaNoBranco}
          formatarDinheiro={formatarDinheiro}
          controlaEstoque={controlaEstoque}
          unidadeDeVenda={unidadeDeVenda}
        />
        {campos}
      </div>
    );
  }

  /* ---- lista com busca: quando são muitas ---- */
  if (atributo.estilo === "lista") {
    return (
      <div className="space-y-2">
        {cabecalho}
        <div className="overflow-hidden rounded-xl border border-zinc-200">
          <div className="flex items-center gap-2 border-b border-zinc-100 px-3">
            <Search size={15} className="shrink-0 text-zinc-400" />
            <input
              value={busca}
              onChange={(evento) => setBusca(evento.target.value)}
              placeholder={`Buscar entre ${atributo.opcoes.length} opções`}
              className="h-10 flex-1 border-0 bg-transparent text-sm outline-none placeholder:text-zinc-400"
            />
            {busca ? (
              <button type="button" onClick={() => setBusca("")} aria-label="Limpar busca" className="text-zinc-400">
                <X size={14} />
              </button>
            ) : null}
          </div>
          <div className="max-h-[236px] overflow-y-auto">
            {visiveis.length === 0 ? (
              <p className="px-3 py-6 text-center text-[13px] text-zinc-500">Nada com “{busca}”.</p>
            ) : (
              visiveis.map((opcao) => {
                const marcada = escolhidas.includes(opcao.label);
                const fora = esgotada(opcao, controlaEstoque);
                const preco = precoDaOpcao(opcao);
                return (
                  <button
                    key={opcao.label}
                    type="button"
                    onClick={() => !fora && marcar(opcao)}
                    disabled={fora}
                    aria-pressed={marcada}
                    className={`flex w-full items-center gap-2.5 border-b border-zinc-100 px-3 py-2.5 text-left text-sm last:border-b-0 ${
                      fora ? "cursor-not-allowed text-zinc-400" : "hover:bg-zinc-50"
                    }`}
                    style={marcada ? { backgroundColor: `${corDaLoja}12` } : undefined}
                  >
                    <span
                      className="flex h-4 w-4 shrink-0 items-center justify-center rounded-full"
                      style={{
                        boxShadow: marcada ? "none" : "inset 0 0 0 1.5px rgba(24,24,27,0.22)",
                        backgroundColor: marcada ? corDaLoja : "transparent",
                        color: corDoTextoNaLoja,
                      }}
                    >
                      {marcada ? <Check size={11} strokeWidth={3} /> : null}
                    </span>
                    <span className={`flex-1 truncate ${fora ? "line-through" : ""}`}>{opcao.label}</span>
                    {fora ? <span className="text-[11px]">esgotado</span> : null}
                    {preco ? <span className="text-[12px] font-medium text-zinc-500">{preco}</span> : null}
                  </button>
                );
              })
            )}
          </div>
        </div>
        {campos}
      </div>
    );
  }

  /* ---- botões: o caso comum ---- */
  return (
    <div className="space-y-2">
      {cabecalho}
      <div className="flex flex-wrap gap-1.5">
        {atributo.opcoes.map((opcao) => {
          const marcada = escolhidas.includes(opcao.label);
          const fora = esgotada(opcao, controlaEstoque);
          const preco = precoDaOpcao(opcao);
          return (
            <button
              key={opcao.label}
              type="button"
              onClick={() => !fora && marcar(opcao)}
              disabled={fora}
              aria-pressed={marcada}
              className={`inline-flex min-h-[36px] items-center gap-1.5 rounded-lg border px-3 py-1.5 text-[13px] transition-colors ${
                fora
                  ? "cursor-not-allowed border-zinc-200 bg-zinc-50 text-zinc-400 line-through"
                  : marcada
                    ? "font-semibold"
                    : "border-zinc-200 bg-white text-zinc-700 hover:border-zinc-300 hover:bg-zinc-50"
              }`}
              style={
                marcada && !fora
                  ? { backgroundColor: corDaLoja, borderColor: corDaLoja, color: corDoTextoNaLoja }
                  : undefined
              }
            >
              <span>{opcao.label}</span>
              {preco ? (
                <span
                  className={`text-[11px] ${marcada && !fora ? "opacity-80" : "text-zinc-500"}`}
                  style={marcada && !fora ? { color: corDoTextoNaLoja } : undefined}
                >
                  {preco}
                </span>
              ) : null}
            </button>
          );
        })}
      </div>
      {campos}
    </div>
  );
}

/* ---------------------------------------------------------------- *
 *  O conjunto
 * ---------------------------------------------------------------- */

export function SeletorDeVariacoes({
  atributos,
  selecionadas,
  aoEscolher,
  corDaLoja,
  corDoTextoNaLoja,
  corDaLojaNoBranco,
  formatarDinheiro,
  imagens,
  atributoDeEstoque,
  camposDaOpcao,
  unidadeDeVenda,
}: Props) {
  const { escolhas, informativos } = useMemo(() => variacoesDaVitrine(atributos), [atributos]);
  const [abertoAMao, setAbertoAMao] = useState<string | null>(null);
  /* O atributo em que a pessoa está mexendo agora. Serve à escolha múltipla:
     ela fica aberta enquanto for a da vez, e só recolhe quando a atenção
     passa para outra. */
  const [emUso, setEmUso] = useState<string | null>(null);

  const mapaDeImagens = useMemo(
    () => new Map((imagens || []).map((imagem) => [imagem.id, imagem.image_url])),
    [imagens]
  );
  const nomeDoEstoque = semAcento(atributoDeEstoque || "");

  /* Com três ou mais escolhas a página vira rolagem: o resolvido recolhe numa
     linha e o que falta fica à vista. Com uma ou duas, tudo aberto. */
  const recolhe = escolhas.length >= A_PARTIR_DE_QUANTAS_RECOLHE;

  /* O atributo aberto vira chip sozinho quando a atenção sai dele: um clique
     em qualquer outro lugar, o foco indo para outro campo, ou a página
     rolando. Sem isto o último atributo mexido ficava aberto para sempre —
     o cliente escolhia e a lista continuava lá, ocupando a tela. */
  useEffect(() => {
    if (!recolhe) return;
    if (!abertoAMao && !emUso) return;

    const aindaEDele = (alvo: EventTarget | null) => {
      const elemento = alvo instanceof Element ? alvo : null;
      const id = elemento?.closest("[data-ks-variacao]")?.getAttribute("data-ks-variacao");
      return Boolean(id && (id === abertoAMao || id === emUso));
    };
    const recolher = () => {
      setAbertoAMao(null);
      setEmUso(null);
    };
    const aoApontar = (evento: Event) => {
      if (!aindaEDele(evento.target)) recolher();
    };
    const aoFocar = (evento: FocusEvent) => {
      if (!aindaEDele(evento.target)) recolher();
    };
    /* Rolar um fio ao escolher é comum e não pode fechar nada; a folga de 60
       pixels separa o tranco do dedo da intenção de seguir em frente. */
    const inicio = window.scrollY;
    const aoRolar = () => {
      if (Math.abs(window.scrollY - inicio) >= 60) recolher();
    };

    document.addEventListener("pointerdown", aoApontar, true);
    document.addEventListener("focusin", aoFocar, true);
    window.addEventListener("scroll", aoRolar, { passive: true });
    return () => {
      document.removeEventListener("pointerdown", aoApontar, true);
      document.removeEventListener("focusin", aoFocar, true);
      window.removeEventListener("scroll", aoRolar);
    };
  }, [recolhe, abertoAMao, emUso]);

  if (!escolhas.length && !informativos.length) return null;

  return (
    <div className="space-y-3">
      {escolhas.map((atributo) => {
        const escolhidas = selecionadas[atributo.nomeOriginal] || [];
        const resolvido = escolhidas.length > 0;
        const aberto =
          !recolhe ||
          !resolvido ||
          abertoAMao === atributo.id ||
          /* Múltipla escolha continua aberta enquanto for a da vez. */
          (atributo.multipla && emUso === atributo.id);

        if (!aberto) {
          return (
            <button
              key={atributo.id}
              type="button"
              data-ks-variacao={atributo.id}
              onClick={() => {
                setAbertoAMao(atributo.id);
                setEmUso(atributo.id);
              }}
              className="group flex w-full items-center gap-2.5 border-b border-zinc-100 py-2 text-left transition-colors hover:border-zinc-200"
            >
              <IconeDaVariacao nome={atributo.nomeOriginal} size={14} className="shrink-0 text-zinc-400" />
              <span className="shrink-0 text-[11px] font-semibold uppercase tracking-[0.08em] text-zinc-400">
                {atributo.nome}
              </span>
              <span className="min-w-0 flex-1 truncate text-[13.5px] font-medium text-zinc-900">
                {escolhidas.join(", ")}
              </span>
              <span
                className="inline-flex shrink-0 items-center gap-0.5 text-[12px] font-medium opacity-70 transition-opacity group-hover:opacity-100"
                style={{ color: corDaLojaNoBranco }}
              >
                trocar
                <ChevronDown size={12} />
              </span>
            </button>
          );
        }

        return (
          <div key={atributo.id} data-ks-variacao={atributo.id}>
          <Atributo
            atributo={atributo}
            escolhidas={escolhidas}
            aoEscolher={(nome, rotulo, multipla, obrigatoria) => {
              aoEscolher(nome, rotulo, multipla, obrigatoria);
              /* A da vez passa a ser esta; a anterior, se estava aberta só
                 por ser múltipla, recolhe sozinha. */
              setEmUso(atributo.id);
              setAbertoAMao(multipla ? atributo.id : null);
            }}
            corDaLoja={corDaLoja}
            corDoTextoNaLoja={corDoTextoNaLoja}
            corDaLojaNoBranco={corDaLojaNoBranco}
            formatarDinheiro={formatarDinheiro}
            unidadeDeVenda={unidadeDeVenda}
            imagens={mapaDeImagens}
            controlaEstoque={Boolean(nomeDoEstoque) && semAcento(atributo.nomeOriginal) === nomeDoEstoque}
            campos={camposDaOpcao ? camposDaOpcao(atributo.nomeOriginal) : null}
          />
          </div>
        );
      })}

      {/* O que não é escolha: uma linha de texto, não um botão. */}
      {informativos.length > 0 ? (
        <div className="flex flex-wrap gap-x-4 gap-y-1 pt-0.5">
          {informativos.map((atributo) => (
            <span key={atributo.id} className="text-[12px] text-zinc-500">
              {atributo.nome}: <span className="font-medium text-zinc-700">{atributo.opcoes[0].label}</span>
            </span>
          ))}
        </div>
      ) : null}
    </div>
  );
}
