import type { DocumentoDoBanner } from "./documento";
import { formatoDaChave } from "./formatos";
import type { ModeloDoEditor } from "./modelos";
import MINIATURAS_DOS_MODELOS from "./miniaturas-dos-modelos.json";

/*
 * As miniaturas como IMAGEM (o jeito do Canva): a galeria mostra uma foto
 * pronta de cada modelo e de cada design, e as peças soltas só são montadas
 * quando a arte abre no editor. Assim a página fica leve com milhares de
 * artes.
 *
 * - Modelos da casa: fotografados uma vez (.cache/banner/miniaturas-dos-modelos.mts)
 *   e guardados no depósito; a lista fica em miniaturas-dos-modelos.json com
 *   a marca do documento de cada um. Modelo que mudou no código (marca
 *   diferente) volta a ser montado na hora, até a foto nova ser tirada.
 * - Designs da empresa: a rota /api/artes/miniatura fotografa a primeira
 *   página no servidor e guarda em editor-artes/<id>/<carimbo>.webp, onde o
 *   carimbo é a hora da última gravação (mudou a arte, muda o endereço).
 */

export const DEPOSITO_PUBLICO = "https://ptpstwltddugnnlewvbw.supabase.co/storage/v1/object/public/products";

/* ------------------------------------------------------- tipos de modelo */

/* As categorias de modelos (como as do Canva): cada uma abre no formato dela e mostra só os modelos feitos para ela. */
export type TipoDeModelo = { chave: string; nome: string; formato: string; cor: string; fundo: string; filtro: (m: ModeloDoEditor) => boolean };

export const TIPOS_DE_MODELO: TipoDeModelo[] = [
  { chave: "post", nome: "Post para Instagram", formato: "post-retrato", cor: "#BE185D", fundo: "#FCE7F3", filtro: (m) => m.feitoPara === "post" },
  { chave: "carrossel", nome: "Carrossel", formato: "post-retrato", cor: "#6D28D9", fundo: "#EDE9FE", filtro: (m) => m.feitoPara === "carrossel" },
  { chave: "story", nome: "Story e Reels", formato: "story", cor: "#C2410C", fundo: "#FFEDD5", filtro: (m) => m.feitoPara === "story" },
  { chave: "banner", nome: "Banner da loja", formato: "banner-loja", cor: "#1D4ED8", fundo: "#DBEAFE", filtro: (m) => m.feitoPara === "faixa" },
  { chave: "caneca", nome: "Caneca", formato: "caneca", cor: "#047857", fundo: "#D1FAE5", filtro: (m) => m.feitoPara === "caneca" },
  { chave: "calendario", nome: "Calendário", formato: "a4-deitada", cor: "#B91C1C", fundo: "#FEE2E2", filtro: (m) => m.feitoPara === "a4-deitada" },
];

/** O pedido neutro (sem a cara de nenhuma loja) com que as miniaturas dos modelos são tiradas. */
export function pedidoNeutro(t: TipoDeModelo) {
  const f = formatoDaChave(t.formato)!;
  return { largura: f.largura, altura: f.altura, cor: "#7C3AED", logo: null, proporcaoDoLogo: null, foto: null };
}

/* ---------------------------------------------------- modelos da casa */

/**
 * A marca do documento: o mesmo desenho dá a mesma marca (os ids sorteados a
 * cada montagem ficam de fora). FNV-1a de 32 bits, em hexadecimal.
 */
export function marcaDoDocumento(doc: DocumentoDoBanner): string {
  const ids = new Map<string, number>();
  const texto = JSON.stringify(doc, (chave, valor) => {
    if ((chave === "id" || chave === "grupo" || chave === "dentroDe" || chave === "quadrosDe") && typeof valor === "string") {
      if (!ids.has(valor)) ids.set(valor, ids.size);
      return ids.get(valor);
    }
    return valor;
  });
  let h = 0x811c9dc5;
  for (let i = 0; i < texto.length; i += 1) {
    h ^= texto.charCodeAt(i);
    h = Math.imul(h, 0x01000193);
  }
  return (h >>> 0).toString(16).padStart(8, "0");
}

export type FotoPronta = { src: string; largura: number; altura: number };
/* `paginas`: a foto de cada página (carrossel e modelos com mais de uma); a primeira é a de `src`. */
export type Guardada = FotoPronta & {
  marca: string;
  paginas?: FotoPronta[];
  /* As cores que mais aparecem nas fotos (etiquetas.ts). */
  cores?: string[];
  /* Modelo animado: a prévia em MP4 (sem som, leve) que a galeria toca em loop; `marca` é a do desenho gravado. */
  video?: { src: string; marca: string; duracao: number } | null;
};

/** As fotos de todas as páginas do modelo (uma só quando ele tem uma página). */
export const fotosDasPaginas = (g: Guardada): FotoPronta[] => (g.paginas?.length ? g.paginas : [g]);
const GUARDADAS = MINIATURAS_DOS_MODELOS as Record<string, Guardada>;

/** A chave de um modelo na lista das miniaturas (o mesmo modelo pode estar em mais de um tipo). */
export const chaveDaMiniatura = (tipo: string, modelo: string) => `${tipo}/${modelo}`;

/** A foto pronta do modelo, se ela ainda é do desenho de agora. */
export function miniaturaDoModelo(tipo: string, modelo: string, doc: DocumentoDoBanner): Guardada | null {
  const guardada = GUARDADAS[chaveDaMiniatura(tipo, modelo)];
  return guardada && guardada.marca === marcaDoDocumento(doc) ? guardada : null;
}

/* ----------------------------------------------------- designs da empresa */

/** Onde a foto do design fica (muda a cada gravação). */
export function enderecoDaMiniatura(arte: { id: string; updated_at: string }) {
  return `${DEPOSITO_PUBLICO}/editor-artes/${arte.id}/${Date.parse(arte.updated_at) || 0}.webp`;
}

/* Fila: uma foto pedida por vez nesta aba (o Chrome do servidor é um só). */
const fila: Array<() => void> = [];
let tirando = 0;
const pedidas = new Map<string, Promise<string | null>>();

function naFila<T>(tarefa: () => Promise<T>): Promise<T> {
  return new Promise((resolver, rejeitar) => {
    const rodar = () => {
      tirando += 1;
      tarefa()
        .then(resolver, rejeitar)
        .finally(() => {
          tirando -= 1;
          fila.shift()?.();
        });
    };
    if (tirando < 1) rodar();
    else fila.push(rodar);
  });
}

/** Pede ao servidor a foto do design (uma vez por versão); volta o endereço ou null. */
export function pedirMiniatura(arte: { id: string; updated_at: string }): Promise<string | null> {
  const chave = `${arte.id}@${arte.updated_at}`;
  const ja = pedidas.get(chave);
  if (ja) return ja;
  const pedido = naFila(async () => {
    /* O servidor só tira a foto com o Chrome livre (baixar a arte vem antes): tenta por até ~1 minuto. */
    for (let tentativa = 0; tentativa < 8; tentativa += 1) {
      const resposta = await fetch("/api/artes/miniatura", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ id: arte.id }) }).catch(() => null);
      if (resposta?.ok) return ((await resposta.json().catch(() => ({}))) as { src?: string }).src || null;
      /* Servidor ocupado: espera um pouco e tenta de novo. */
      if (resposta && resposta.status !== 503) return null;
      await new Promise((r) => setTimeout(r, Math.min(12000, 1500 * (tentativa + 1)) + Math.random() * 800));
    }
    return null;
  });
  pedidas.set(chave, pedido);
  return pedido;
}
