import { SEM_FILTROS, novoId, type PecaDeImagem } from "./documento";

/*
 * A biblioteca de imagens do editor: recortes em PNG (fundo transparente,
 * do PurePNG, conferidos um a um: sem marca, personagem, gente famosa nem
 * nudez), os ícones 3D do 3dicons (CC0), cada um em 4 cores e 3 ângulos, e
 * os elementos decorativos em SVG (Openclipart e domínio público do
 * Wikimedia), com as cores trocáveis (vetores.ts). Os SVGs têm o índice
 * próprio (public/editor/vetores.json, de .cache/vetores/scripts) e entram
 * juntos na mesma lista e na mesma busca.
 * Os arquivos moram no depósito (products/editor-modelos/biblioteca, gerados
 * por .cache/biblioteca/scripts) e o índice em public/editor/biblioteca.json
 * (nome e palavras de busca em português). A busca acha pelo genérico
 * ("comida", "animal", "natal") e pelo específico ("torre eiffel").
 */

export const DEPOSITO_DA_BIBLIOTECA = "https://ptpstwltddugnnlewvbw.supabase.co/storage/v1/object/public/products/editor-modelos/biblioteca";

/* c: código; n: nome; g: grupo; t: palavras de busca; p: largura/altura; v: as variações (só os ícones 3D); k: as cores (só os SVGs). */
export type ItemDaBiblioteca = { c: string; n: string; g: string; t: string; p: number; v?: string[]; k?: string[] };
export type IndiceDaBiblioteca = { grupos: Record<string, string>; lista: ItemDaBiblioteca[] };

/* A ordem dos grupos na tela: os ícones 3D primeiro, depois os recortes. */
export const GRUPOS_DOS_ICONES_3D = ["3d-geral", "3d-natal", "3d-halloween", "3d-redes"];
export const ehIcone3d = (i: Pick<ItemDaBiblioteca, "g">) => i.g.startsWith("3d-");
/* Os elementos decorativos em SVG (cor trocável). */
export const GRUPOS_DOS_ORNAMENTOS = ["ornamentos", "molduras-svg", "cantos", "divisores", "flores-svg", "folhas-svg", "guirlandas", "vinhetas", "espirais"];
export const ehVetor = (i: Pick<ItemDaBiblioteca, "k">) => Boolean(i.k?.length);
export const GRUPOS_DOS_RECORTES = ["pessoas", "mulheres", "homens", "criancas", "maos", "animais", "comida", "natureza", "efeitos", "clipart", "negocios", "roupas", "moda", "objetos", "eletronicos", "esportes", "transporte", "lugares", "renders", "simbolos", "diversos"];

/* As variações dos ícones 3D: o ângulo da câmera e o acabamento. */
export const ANGULOS_3D: Array<{ chave: string; nome: string }> = [
  { chave: "dynamic", nome: "Diagonal" },
  { chave: "front", nome: "Frente" },
  { chave: "iso", nome: "Isométrico" },
];
export const CORES_3D: Array<{ chave: string; nome: string }> = [
  { chave: "color", nome: "Colorido" },
  { chave: "gradient", nome: "Degradê" },
  { chave: "premium", nome: "Premium" },
  { chave: "clay", nome: "Argila" },
];

/** A variação que entra quando não se escolhe nenhuma (colorido, na diagonal). */
export const variantePadrao = (i: ItemDaBiblioteca) => (i.v?.includes("dynamic_color") ? "dynamic_color" : i.v?.[0]);

export const urlDoItem = (i: ItemDaBiblioteca, variante?: string) => (i.k ? `${DEPOSITO_DA_BIBLIOTECA}/vetores/${i.c}.svg` : i.v ? `${DEPOSITO_DA_BIBLIOTECA}/${i.c}/${variante ?? variantePadrao(i)}.webp` : `${DEPOSITO_DA_BIBLIOTECA}/${i.c}.webp`);
export const miniDoItem = (i: ItemDaBiblioteca, variante?: string) => (i.k ? `${DEPOSITO_DA_BIBLIOTECA}/vetores/mini/${i.c}.webp` : i.v ? `${DEPOSITO_DA_BIBLIOTECA}/${i.c}/mini-${variante ?? variantePadrao(i)}.webp` : `${DEPOSITO_DA_BIBLIOTECA}/mini/${i.c}.webp`);

/** Os dois índices numa lista só (os recortes e ícones 3D; os SVGs). */
export function juntarIndices(...indices: Array<IndiceDaBiblioteca | null | undefined>): IndiceDaBiblioteca {
  return { grupos: Object.assign({}, ...indices.map((i) => i?.grupos || {})), lista: indices.flatMap((i) => i?.lista || []) };
}

let indice: Promise<IndiceDaBiblioteca> | null = null;
/** O índice (baixa uma vez por visita). Sem o dos SVGs, fica o resto. */
export function carregarBiblioteca() {
  const ler = (u: string) => fetch(u).then((r) => (r.ok ? (r.json() as Promise<IndiceDaBiblioteca>) : Promise.reject(new Error("sem índice"))));
  indice ??= Promise.all([ler("/editor/biblioteca.json"), ler("/editor/vetores.json").catch(() => null)])
    .then(([a, b]) => juntarIndices(a, b))
    .catch((erro) => {
      indice = null;
      throw erro;
    });
  return indice;
}

const semAcento = (s: string) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
/* A palavra sem o plural ("flores" → "flor", "corações" → "coracao", "bolos" → "bolo"). */
function raiz(p: string) {
  if (p.length > 4 && p.endsWith("oes")) return `${p.slice(0, -3)}ao`;
  if (p.length > 4 && p.endsWith("aes")) return `${p.slice(0, -3)}ao`;
  if (p.length > 4 && /[rsz]es$/.test(p)) return p.slice(0, -2);
  if (p.length > 3 && p.endsWith("s")) return p.slice(0, -1);
  return p;
}

/**
 * A busca: cada palavra precisa aparecer no nome, nas palavras de busca ou no
 * nome do grupo (sem acento, plural vale como singular). O nome pesa mais
 * (o começo do nome mais ainda), para "gato" trazer o gato antes do que só
 * tem "gato" nas palavras de busca.
 */
export function buscarNaBiblioteca(i: IndiceDaBiblioteca, termo: string, grupos?: string[]): ItemDaBiblioteca[] {
  const palavras = semAcento(termo).split(/[^a-z0-9]+/).filter((p) => p.length > 1).map(raiz);
  if (!palavras.length) return [];
  const achados: Array<{ item: ItemDaBiblioteca; nota: number }> = [];
  for (const item of i.lista) {
    if (grupos && !grupos.includes(item.g)) continue;
    const nome = semAcento(item.n);
    const resto = `${semAcento(item.t)} ${semAcento(i.grupos[item.g] || "")} ${item.c.replace(/-/g, " ")}`;
    let nota = 0;
    let todas = true;
    for (const p of palavras) {
      const noNome = nome.split(/\s+/).some((w) => raiz(w).startsWith(p)) ? 3 : nome.includes(p) ? 2 : 0;
      const noResto = noNome ? 0 : resto.split(/\s+/).some((w) => raiz(w).startsWith(p)) ? 1 : resto.includes(p) ? 0.5 : 0;
      if (!noNome && !noResto) {
        todas = false;
        break;
      }
      nota += noNome + noResto + (nome.startsWith(p) ? 1 : 0);
    }
    if (todas) achados.push({ item, nota });
  }
  return achados.sort((a, b) => b.nota - a.nota || a.item.n.localeCompare(b.item.n)).map((a) => a.item);
}

/** A peça na arte: no meio, sem sombra nem canto arredondado (o recorte é transparente). */
export function pecaDaBiblioteca(m: { largura: number; altura: number }, item: ItemDaBiblioteca, variante?: string): PecaDeImagem {
  const lado = Math.min(m.largura, m.altura);
  const proporcao = Math.min(6, Math.max(0.16, item.p || 1));
  const h = lado * (ehIcone3d(item) ? 0.34 : ehVetor(item) ? (proporcao > 2.5 ? 0.18 : 0.5) : 0.55);
  const w = h * proporcao;
  const largura = Math.min(w, m.largura * 0.9);
  const altura = largura / proporcao;
  return {
    id: novoId("i"),
    tipo: "imagem",
    nome: item.n,
    x: (m.largura - largura) / 2,
    y: (m.altura - altura) / 2,
    w: largura,
    h: altura,
    rot: 0,
    opacidade: 1,
    src: urlDoItem(item, variante),
    papel: "foto",
    ajuste: "conter",
    raio: 0,
    circulo: false,
    borda: null,
    sombra: null,
    filtros: { ...SEM_FILTROS },
    ...(item.k?.length ? { vetor: { cores: item.k } } : {}),
  };
}
