/*
 * O que os modelos do editor de artes usam para se montar: peças no jeito
 * curto (texto, forma, imagem), o fundo como camadas e a coluna empilhada que
 * encolhe junto quando não cabe. Separado de modelos.ts para os modelos
 * profissionais (modelos-pro.ts) usarem sem import circular.
 */
import {
  FONTE_PADRAO,
  FUNDO_BASE,
  SEM_FILTROS,
  camadasDoFundo,
  degrade,
  luminancia,
  misturar,
  novoId,
  solida,
  sombraDura,
  sombraSuave,
  type DocumentoDoBanner,
  type FundoDoBanner,
  type FundoDoTexto,
  type Peca,
  type PecaDeForma,
  type PecaDeImagem,
  type PecaDeTexto,
  type TexturaDoFundo,
} from "./documento";

export type PedidoDoModelo = {
  largura: number;
  altura: number;
  /* A cor da loja (#RRGGBB). */
  cor: string;
  logo: string | null;
  /* Largura ÷ altura do logo, quando se sabe. */
  proporcaoDoLogo?: number | null;
  foto: string | null;
};

export const AMARELO = "#FFD400";
export const VERDE = "#00A650";
export const QUASE_PRETO = "#141414";

/* Os modelos escrevem do jeito curto (cor, fundo com forma e cor, sombra por nome); aqui vira a peça completa. */
export type CamposDoTexto = Omit<Partial<PecaDeTexto>, "fundo" | "sombra"> &
  Pick<PecaDeTexto, "texto" | "tamanho"> & { cor?: string; fundo?: { forma: FundoDoTexto["forma"]; cor: string; cor2?: string | null; raio?: number } | null; sombra?: "dura" | "suave" | null };

export function fundoDoTexto(f: NonNullable<CamposDoTexto["fundo"]>): FundoDoTexto {
  return {
    forma: f.forma,
    pintura: f.cor2 ? degrade(f.cor, f.cor2, 90) : solida(f.cor),
    borda: null,
    raio: f.raio ?? 16,
    pontas: f.forma === "estrela" ? 5 : 16,
    profundidade: f.forma === "estrela" ? 0.55 : 0.2,
  };
}

export function texto(campos: CamposDoTexto): PecaDeTexto {
  const { cor, fundo, sombra, ...resto } = campos;
  return {
    id: novoId("t"),
    tipo: "texto",
    x: 0,
    y: 0,
    w: 100,
    h: 100,
    rot: 0,
    opacidade: 1,
    fonte: FONTE_PADRAO,
    peso: 800,
    pintura: solida(cor || "#FFFFFF"),
    alinhamento: "esquerda",
    vertical: "meio",
    italico: false,
    caixaAlta: false,
    riscado: false,
    sublinhado: false,
    espaco: 0,
    linha: 1.05,
    fundo: fundo ? fundoDoTexto(fundo) : null,
    contorno: null,
    sombra: sombra === "dura" ? sombraDura() : sombra === "suave" ? sombraSuave() : null,
    ...resto,
  };
}

export type CamposDaForma = Omit<Partial<PecaDeForma>, "sombra"> & Pick<PecaDeForma, "forma" | "x" | "y" | "w" | "h"> & { cor: string; cor2?: string | null; sombra?: boolean };

export function forma(campos: CamposDaForma): PecaDeForma {
  const { cor, cor2, sombra, ...resto } = campos;
  return { id: novoId("f"), tipo: "forma", rot: 0, opacidade: 1, pintura: cor2 ? degrade(cor, cor2) : solida(cor), raio: 0, borda: null, sombra: sombra ? sombraSuave(0.22) : null, pontas: 16, profundidade: 0.2, ...resto };
}

export type CamposDaImagem = Omit<Partial<PecaDeImagem>, "sombra"> & Pick<PecaDeImagem, "src"> & { sombra?: boolean };

export function imagem(campos: CamposDaImagem): PecaDeImagem {
  const { sombra = true, ...resto } = campos;
  return { id: novoId("i"), tipo: "imagem", x: 0, y: 0, w: 100, h: 100, rot: 0, opacidade: 1, papel: "foto", ajuste: "conter", raio: 0, circulo: false, borda: null, sombra: sombra ? sombraSuave() : null, filtros: { ...SEM_FILTROS }, ...resto };
}

/* O banner pronto: o fundo (cor e textura) vira as primeiras camadas, travadas no lugar. */
export function documento(largura: number, altura: number, fundo: FundoDoBanner, pecas: Peca[]): DocumentoDoBanner {
  return { versao: 3, largura, altura, fundo: FUNDO_BASE, pecas: [...camadasDoFundo(largura, altura, fundo.pintura, fundo.textura), ...pecas] };
}

/* O fundo do jeito curto dos modelos. */
export function fundoDoBanner(f: { cor: string; cor2?: string | null; angulo?: number; textura: TexturaDoFundo["tipo"]; corDaTextura?: string }): FundoDoBanner {
  return {
    pintura: f.cor2 ? degrade(f.cor, f.cor2, f.angulo ?? 135) : solida(f.cor),
    textura: { tipo: f.textura, cor: f.corDaTextura || "#FFFFFF", opacidade: f.textura === "grao" ? 0.16 : 0.13, quantidade: 22, tamanho: 0, centroX: 70, centroY: 50, giro: 0 },
  };
}

/* ------------------------------------------------------------ empilhar */

/* Uma linha da coluna: a peça, a largura e a altura desejadas e o respiro depois. "fixa" = a largura acompanha a escala (pílula, logo, foto). */
export type Linha = { peca: PecaDeTexto | PecaDeImagem; w: number; h: number; depois?: number; fixa?: boolean };

/**
 * Põe as linhas uma embaixo da outra entre `topo` e `base`, centradas na
 * vertical. Se não couberem, tudo diminui junto (altura, letra e, nas fixas,
 * a largura). `x` é a borda esquerda da coluna; `largura`, a da coluna;
 * `centro` alinha cada linha no meio dela.
 */
export function empilhar(linhas: Linha[], x: number, largura: number, topo: number, base: number, centro: boolean) {
  const total = linhas.reduce((soma, l, i) => soma + l.h + (i < linhas.length - 1 ? l.depois ?? 0 : 0), 0);
  const f = Math.min(1, (base - topo) / Math.max(1, total));
  let y = topo + (base - topo - total * f) / 2;
  for (const l of linhas) {
    const p = l.peca;
    p.h = l.h * f;
    p.w = l.fixa ? l.w * f : Math.min(largura, l.w);
    p.x = centro ? x + (largura - p.w) / 2 : x;
    p.y = y;
    if (p.tipo === "texto") p.tamanho *= f;
    y += p.h + (l.depois ?? 0) * f;
  }
}

export function linhaDoLogo(p: PedidoDoModelo, altura: number, depois: number): Linha[] {
  if (!p.logo) return [];
  const proporcao = Math.min(6, Math.max(0.6, p.proporcaoDoLogo || 3));
  return [{ peca: imagem({ nome: "Logo", src: p.logo, papel: "logo", sombra: false }), w: altura * proporcao, h: altura, depois, fixa: true }];
}

/** Uma cor da loja forte o bastante para fundo de oferta (as muito claras escurecem). */
export function corDeFundo(cor: string) {
  return luminancia(cor) > 0.5 ? misturar(cor, "#000000", 0.45) : cor;
}

