/*
 * Modelos animados "em ritmo" (out/2026), no jeito dos reels que viralizam:
 * legenda que entra palavra por palavra em chicote, marca-texto, fundo escuro
 * de mármore / papel quadriculado / lima, manchas que dançam, cursor que anda
 * e clica, transições em círculo, chicote e apagar, trilha e swoosh em cada
 * passagem. "Produto em movimento" mostra os quadros-chave (a foto viaja,
 * cresce e gira), as máscaras que andam e os elementos prontos para vídeo.
 * As fotos são molduras: a pessoa solta a dela em cima ou clica em Substituir.
 */
import { animacaoNova, type AnimacaoDaPeca, type EfeitoDaAnimacao, type MovimentoDoFundo, type QuadroChave, type TransicaoDaPagina } from "./animacao";
import { temposDoProjeto } from "./cena";
import { documentoDasPaginas, novoId, type AudioDoProjeto, type DocumentoDoBanner, type Peca, type PecaDeImagem } from "./documento";
import { elementoDeVideo, encaixar, manchaDeVideo } from "./elementos-de-video";
import { documento, fundoDoBanner, type PedidoDoModelo } from "./modelos-base";
import { comAlfa, f, foto, foto$, ic, marca, nada, radial, sombraDe, t, tex } from "./modelos-ramos";
import SONS from "./sons-da-biblioteca.json";

const W = 1080;
const H = 1920;
const LIMA = "#C6F432";
const NOITE = "#0B0D09";
const PAPEL = "#EEF0EA";
const TINTA = "#0B0D09";

type Extra = Partial<AnimacaoDaPeca>;
function anima<T extends Peca>(pecas: T | T[], entrada: EfeitoDaAnimacao | null, inicio: number, extra: Extra = {}): T[] {
  const lista = Array.isArray(pecas) ? pecas : [pecas];
  const grupo = lista.length > 1 ? novoId("g") : undefined;
  const a = animacaoNova(entrada, { inicio, ...extra });
  return lista.map((p) => ({ ...p, animacao: a, ...(grupo ? { grupo } : {}) }));
}
function cena(doc: DocumentoDoBanner, duracao: number, fundo: MovimentoDoFundo, transicao: TransicaoDaPagina | null): DocumentoDoBanner {
  return { ...doc, animacao: { duracao, fundo }, ...(transicao ? { transicao } : {}) };
}
/*
 * Um elemento pronto (elementos-de-video.ts) encaixado na caixa, com os
 * tempos empurrados `atraso` segundos; `fotos` enchem as molduras dele em
 * ordem (a pessoa troca pelo Substituir).
 */
function elemento(chave: string, caixa: { x: number; y: number; w: number; h: number }, atraso = 0, fotos: string[] = []): Peca[] {
  let k = 0;
  return encaixar(elementoDeVideo(chave).criar({ largura: W, altura: H }), caixa).map((p0) => {
    const p = p0.tipo === "imagem" && p0.moldura && k < fotos.length ? ({ ...p0, src: foto$(fotos[k++]), moldura: false, nome: "Foto" } as Peca) : p0;
    const a = p.animacao;
    if (!a || !atraso) return p;
    return { ...p, animacao: { ...a, inicio: a.inicio + atraso, fim: a.fim === null ? null : a.fim + atraso, mover: a.mover ? { ...a.mover, inicio: a.mover.inicio + atraso } : a.mover } } as Peca;
  });
}

/* ------------------------------------------------------------- fundos */

const escuro = (pecas: Peca[]) =>
  documento(W, H, fundoDoBanner({ cor: NOITE, textura: "nenhuma" }), [
    tex("Mármore", "topografia", { x: -120, y: -120, w: W + 240, h: H + 240 }, "#3E5622", { opacidade: 0.9, quantidade: 30, centroX: 28, centroY: 38, giro: 40 }),
    f("Vinheta", "retangulo", { x: 0, y: 0, w: W, h: H }, radial("#000000", "#000000", 0, 0.75)),
    ...pecas,
  ]);
const papel = (pecas: Peca[]) => documento(W, H, fundoDoBanner({ cor: PAPEL, textura: "nenhuma" }), [tex("Quadriculado", "grade", { x: 0, y: 0, w: W, h: H }, "#D3D6CA", { opacidade: 1, tamanho: 64 }), ...pecas]);
const lima = (pecas: Peca[]) =>
  documento(W, H, fundoDoBanner({ cor: LIMA, textura: "nenhuma" }), [
    tex("Quadriculado", "grade", { x: 0, y: 0, w: W, h: H }, "#B2DC1E", { opacidade: 1, tamanho: 64 }),
    f("Brilho", "circulo", { x: -200, y: 400, w: 1480, h: 1100 }, radial("#F3FFB8", "#F3FFB8", 0.55, 0)),
    ...pecas,
  ]);

/* -------------------------------------------------------------- peças */

function fala(nome: string, texto: string, y: number, tamanho: number, cor: string, inicio: number, duracao = 0.7, extra: Extra = {}) {
  const linhas = texto.split("\n").length;
  return anima(t(nome, { x: 40, y, w: 1000, h: Math.round(tamanho * 1.08 * linhas + 10) }, { texto, fonte: "anton", peso: 400, tamanho, linha: 1.02, cor, alinhamento: "centro" }), "chicote", inicio, { porPalavra: true, duracao, ...extra });
}
function marcado(nome: string, texto: string, y: number, tamanho: number, inicio: number, fundo = LIMA, cor = TINTA) {
  const linhas = texto.split("\n").length;
  return anima(
    { ...t(nome, { x: 40, y, w: 1000, h: Math.round(tamanho * 1.26 * linhas + 16) }, { texto, fonte: "anton", peso: 400, tamanho, linha: 1.22, cor, alinhamento: "centro" }), destaque: { pintura: comAlfa(fundo, 1), respiro: 0.14, raio: 6 } },
    "revelar",
    inicio,
    { direcao: "direita", duracao: 0.42 },
  );
}
/* A foto do produto numa moldura inclinada, com cantos e sombra. */
const fotoNaMoldura = (nome: string, arquivo: string, l: { x: number; y: number; w: number; h: number; rot?: number }, extra: Partial<PecaDeImagem> = {}) => foto(nome, foto$(arquivo), l, { raio: 26, sombra: sombraDe("#000000", 34, 70, 0.4), ajuste: "cobrir", ...extra });
const manchas = (cor: string, inicio: number) => [
  ...anima(manchaDeVideo(70, 1780, 190, cor, 1.1), "pop", inicio, { enfase: "balancar", ritmo: 3.2 }),
  ...anima(manchaDeVideo(1030, 1650, 150, cor, 2.6), "pop", inicio + 0.12, { enfase: "flutuar", ritmo: 2.4 }),
];

/* ------------------------------------------------------------- o som */

type Som = { chave: string; nome: string; tipo: "musica" | "efeito"; src: string; duracao: number; onda: number[] };
function audio(chave: string, inicio: number, dura: number, volume: number, extra: Partial<AudioDoProjeto> = {}): AudioDoProjeto | null {
  const s = (SONS as Som[]).find((x) => x.chave === chave);
  if (!s) return null;
  return { id: novoId("a"), nome: s.nome, src: s.src, tipo: s.tipo, duracao: s.duracao, inicio: Math.max(0, Math.round(inicio * 100) / 100), corte: 0, dura: Math.min(dura, s.duracao), volume, suave: false, onda: s.onda, ...extra };
}
/* A trilha do projeto inteiro (com fade in e fade out) e um swoosh em cada passagem; mais os efeitos pedidos e, se houver, a narração (a música desce para a voz aparecer). */
function comTrilha(paginas: DocumentoDoBanner[], musica: string, efeitos: Array<[string, number, number]>, narracao?: { src: string; duracao: number; nome: string }): DocumentoDoBanner {
  const { inicios, total } = temposDoProjeto(paginas);
  const audios = [
    ...(narracao ? [{ id: novoId("a"), nome: narracao.nome, src: narracao.src, tipo: "enviado" as const, duracao: narracao.duracao, inicio: 0, corte: 0, dura: Math.min(narracao.duracao, total), volume: 1, suave: false, fadeDeEntrada: 0, fadeDeSaida: 0.2 }] : []),
    audio(musica, 0, total, narracao ? 0.2 : 0.55, { fadeDeEntrada: 0.5, fadeDeSaida: 1.5 }),
    ...inicios.slice(1).map((i, k) => (paginas[k + 1].transicao ? audio("swoosh-rapido", i - 0.18, 0.7, 0.7) : null)),
    ...efeitos.map(([chave, quando, volume]) => audio(chave, quando, 3, volume)),
  ].filter((a): a is AudioDoProjeto => Boolean(a));
  return documentoDasPaginas(paginas.map((p, i) => (i === 0 ? { ...p, audios } : p)));
}

/* ======================================================= Reels em ritmo */

function reelsEmRitmo(p: PedidoDoModelo): DocumentoDoBanner {
  const c1 = cena(
    escuro([
      ...fala("Criamos", "A GENTE CRIOU", 250, 118, "#FFFFFF", 0.05, 0.6),
      ...marcado("Do zero", "UMA COLEÇÃO\nDO ZERO", 400, 150, 0.75),
      ...anima(fotoNaMoldura("Foto 1", "estampa-preta", { x: 120, y: 880, w: 840, h: 640, rot: -4 }), "aproximar", 0.35, { duracao: 0.6, enfase: "flutuar", ritmo: 3 }),
      ...anima(manchaDeVideo(980, 860, 120, LIMA, 0.4), "pop", 0.6, { enfase: "balancar", ritmo: 2.6 }),
      ...anima(manchaDeVideo(130, 1540, 90, "#7C3AED", 3.3), "pop", 0.75, { enfase: "flutuar", ritmo: 2.2 }),
      ...elemento("v-cursor", { x: 600, y: 1300, w: 380, h: 320 }, 0.85),
    ]),
    2.7,
    "aproximar",
    null,
  );
  const c2 = cena(
    papel([
      ...fala("E agora", "E AGORA TEM", 230, 112, TINTA, 0.25, 0.55),
      ...marcado("Frete", "FRETE GRÁTIS", 370, 160, 0.75),
      ...elemento("v-celular", { x: 300, y: 640, w: 480, h: 920 }, 0.05, ["presente-rosa"]),
      ...anima(t("Selo", { x: 700, y: 700, w: 300, h: 120, rot: 8 }, { texto: "NOVO", fonte: "anton", peso: 400, tamanho: 84, cor: TINTA, alinhamento: "centro", fundo: { forma: "pilula", cor: LIMA } }), "carimbo", 1.25, { duracao: 0.4, enfase: "pulsar", ritmo: 1.2 }),
      ...manchas(TINTA, 0.5),
    ]),
    2.8,
    "parado",
    { tipo: "circulo", duracao: 0.55 },
  );
  const c3 = cena(
    lima([
      ...fala("Se chama", "SE CHAMA", 360, 110, TINTA, 0.25, 0.45),
      ...anima(t("Nome", { x: 40, y: 500, w: 1000, h: 470 }, { texto: "COLEÇÃO\nAURORA", fonte: "anton", peso: 400, tamanho: 215, linha: 1, cor: TINTA, alinhamento: "centro" }), "carimbo", 0.75, { duracao: 0.45 }),
      ...anima(marca(p, 330, 1040, 140, TINTA, "centro"), "pop", 1.2, { enfase: "flutuar", ritmo: 2 }),
      ...["CAMISETAS", "CANECAS", "BONÉS"].flatMap((r, i) =>
        anima(t("Selo", { x: [40, 385, 715][i], y: 1350, w: [330, 300, 290][i], h: 90 }, { texto: r, fonte: "anton", peso: 400, tamanho: 48, cor: LIMA, alinhamento: "centro", fundo: { forma: "pilula", cor: TINTA } }), "pop", 1.5 + i * 0.14, { duracao: 0.35 }),
      ),
    ]),
    2.6,
    "aproximar",
    { tipo: "chicote", duracao: 0.45 },
  );
  const c4 = cena(
    escuro([
      ...fala("Escolhe", "VOCÊ ESCOLHE", 230, 118, "#FFFFFF", 0.3, 0.55),
      ...fala("Estampa", "A ESTAMPA", 370, 200, LIMA, 0.8, 0.5),
      ...elemento("v-navegador", { x: 60, y: 760, w: 960, h: 653 }, 0.2, ["estampa-preta"]),
      ...elemento("v-selecao", { x: 150, y: 940, w: 520, h: 330 }, 1.15),
      ...elemento("v-cursor", { x: 640, y: 1380, w: 380, h: 320 }, 1.0),
    ]),
    2.8,
    "parado",
    { tipo: "circulo", duracao: 0.55 },
  );
  const c5 = cena(
    papel([
      ...fala("Sem pagar", "SEM PAGAR", 330, 130, TINTA, 0.3, 0.45),
      ...anima(t("Caro", { x: 40, y: 490, w: 1000, h: 230 }, { texto: "R$ 79,90", fonte: "anton", peso: 400, tamanho: 210, linha: 1, cor: "#9A9C93", alinhamento: "centro" }), "chicote", 0.7, { duracao: 0.45 }),
      ...anima(f("Risco", "retangulo", { x: 170, y: 590, w: 740, h: 26, rot: -4 }, "#E5262E", { raio: 13 }), "revelar", 1.2, { direcao: "direita", duracao: 0.35 }),
      ...anima(t("Por", { x: 40, y: 900, w: 1000, h: 220, rot: -2 }, { texto: "POR R$ 49,90", fonte: "anton", peso: 400, tamanho: 112, cor: TINTA, alinhamento: "centro", fundo: { forma: "pilula", cor: LIMA } }), "carimbo", 1.55, { duracao: 0.4, enfase: "pulsar", ritmo: 1.3 }),
      ...manchas(TINTA, 0.4),
    ]),
    2.8,
    "parado",
    { tipo: "chicote", duracao: 0.45 },
  );
  const c6 = cena(
    lima([
      ...fala("Não existe", "NÃO EXISTE MAIS", 380, 120, TINTA, 0.35, 0.55),
      ...anima(t("Desculpa", { x: 40, y: 540, w: 1000, h: 280 }, { texto: "DESCULPA", fonte: "anton", peso: 400, tamanho: 250, linha: 1, cor: TINTA, alinhamento: "centro" }), "carimbo", 0.95, { duracao: 0.4, enfase: "tremer", ritmo: 1.1 }),
      ...anima(t("Pra não", { x: 90, y: 840, w: 900, h: 150, rot: -3 }, { texto: "pra não presentear", fonte: "caveat", peso: 700, tamanho: 112, cor: TINTA, alinhamento: "centro" }), "desfocar", 1.45, { duracao: 0.4 }),
      ...anima(manchaDeVideo(140, 1500, 210, TINTA, 4.2), "pop", 0.6, { enfase: "balancar", ritmo: 3 }),
      ...anima(manchaDeVideo(960, 1330, 140, TINTA, 5.4), "pop", 0.75, { enfase: "flutuar", ritmo: 2.4 }),
    ]),
    2.4,
    "aproximar",
    { tipo: "preto", duracao: 0.5 },
  );
  const c7 = cena(
    escuro([
      ...fala("Seu pedido", "SEU PEDIDO", 600, 170, "#FFFFFF", 0.3, 0.45),
      ...marcado("Em 24h", "EM 24 HORAS", 800, 158, 0.8),
      ...elemento("v-selecao", { x: 110, y: 540, w: 860, h: 560 }, 1.15),
      ...elemento("v-cursor", { x: 640, y: 1240, w: 400, h: 340 }, 1.2),
    ]),
    2.4,
    "parado",
    { tipo: "circulo", duracao: 0.55 },
  );
  const c8 = cena(
    papel([
      ...anima(t("Comente", { x: 140, y: 300, w: 800, h: 110 }, { texto: "Comente", fonte: "bricolage-grotesque", peso: 600, tamanho: 96, cor: TINTA, alinhamento: "centro" }), "subir", 0.25),
      ...marcado("Palavra", "EU QUERO", 410, 170, 0.5),
      ...elemento("v-comentario", { x: 100, y: 820, w: 880, h: 225 }, 0.4, ["menina-presente"]),
      ...elemento("v-busca", { x: 120, y: 1120, w: 840, h: 131 }, 0.9),
      ...anima(marca(p, 330, 1420, 100, TINTA, "centro"), "surgir", 1.6),
      ...manchas(TINTA, 0.3),
    ]),
    3.2,
    "parado",
    { tipo: "chicote", duracao: 0.45 },
  );
  const paginas = [c1, c2, c3, c4, c5, c6, c7, c8];
  const { inicios } = temposDoProjeto(paginas);
  return comTrilha(paginas, "fma-loyalty-freak-music-high-technologic-beat-explosion", [
    ["carimbo", inicios[2] + 0.75, 0.6],
    ["carimbo", inicios[5] + 0.95, 0.6],
    ["clique", inicios[0] + 2.0, 0.7],
    ["clique", inicios[3] + 2.1, 0.7],
    ["pop", inicios[1] + 1.25, 0.5],
  ]);
}

/* ================================================== Produto em movimento */

/* Os quadros-chave de uma peça: [segundo, x, y, w, h, giro], com a curva de chegada. */
const quadros = (lista: Array<[number, number, number, number, number, number]>, curva: QuadroChave["curva"] = "suave"): QuadroChave[] => lista.map(([tq, x, y, w, h, rot], i) => ({ t: tq, x, y, w, h, rot, opacidade: 1, curva: i ? curva : "suave" }));

function produtoEmMovimento(p: PedidoDoModelo): DocumentoDoBanner {
  const ROXO = "#5B21B6";
  /* 1. A foto entra pela máscara redonda, cresce e vai para o lado (quadros-chave); o título abre em persianas. */
  const c1 = cena(
    documento(W, H, fundoDoBanner({ cor: "#140C2E", cor2: "#3B1FE0", angulo: 160, textura: "nenhuma" }), [
      tex("Grade", "grade", { x: 0, y: 0, w: W, h: H }, "#FFFFFF", { opacidade: 0.06, tamanho: 72 }),
      ...anima(fotoNaMoldura("Foto do produto", "caneca-roxo", { x: 190, y: 620, w: 700, h: 700 }, { circulo: true, sombra: sombraDe("#000000", 40, 80, 0.45) }), "circulo", 0.2, {
        duracao: 0.7,
        quadros: quadros([[0.9, 190, 620, 700, 700, 0], [2.1, 140, 760, 800, 800, -6], [3.2, 240, 900, 600, 600, 4]], "mola"),
      }),
      ...anima(t("Novidade", { x: 60, y: 230, w: 960, h: 240 }, { texto: "NOVIDADE", fonte: "anton", peso: 400, tamanho: 220, cor: "#FFFFFF", alinhamento: "centro" }), "persiana", 0.5, { duracao: 0.6 }),
      ...anima(t("Chegou", { x: 140, y: 470, w: 800, h: 110 }, { texto: "acabou de chegar", fonte: "caveat", peso: 700, tamanho: 96, cor: LIMA, alinhamento: "centro" }), "diagonal", 1.0, { duracao: 0.5 }),
      ...elemento("v-brilhos", { x: 760, y: 600, w: 240, h: 200 }, 0.9),
    ]),
    3.4,
    "aproximar",
    null,
  );
  /* 2. O celular atravessa a tela e para no meio (quadros-chave), a notificação desce e as curtidas sobem. */
  const celular = elemento("v-celular", { x: 300, y: 560, w: 480, h: 920 }, 0, ["caneca-roxo"]).map((x) => ({ ...x, animacao: undefined }) as Peca);
  const grupo = novoId("g");
  const referencia = celular[0];
  const caminho = quadros([[0, referencia.x + 700, referencia.y + 120, referencia.w, referencia.h, 14], [0.9, referencia.x, referencia.y, referencia.w, referencia.h, -3], [2.6, referencia.x, referencia.y - 40, referencia.w, referencia.h, 0]], "freia");
  const c2 = cena(
    escuro([
      ...celular.map((x) => ({ ...x, grupo, animacao: { ...animacaoNova(null, { inicio: 0 }), quadros: caminho, quadrosDe: referencia.id } }) as Peca),
      ...elemento("v-notificacao", { x: 90, y: 300, w: 900, h: 210 }, 1.0),
      ...elemento("v-curtidas", { x: 720, y: 1280, w: 320, h: 250 }, 1.5),
      ...fala("Já tem", "JÁ ESTÁ NA LOJA", 1560, 110, "#FFFFFF", 0.9, 0.6),
    ]),
    3.2,
    "parado",
    { tipo: "chicote", duracao: 0.45 },
  );
  /* 3. Quem comprou: comentários e as cinco estrelas. */
  const c3 = cena(
    documento(W, H, fundoDoBanner({ cor: "#F4F1FB", textura: "nenhuma" }), [
      tex("Pontos", "pontos", { x: 0, y: 0, w: W, h: H }, ROXO, { opacidade: 0.08, quantidade: 34 }),
      ...fala("Quem comprou", "QUEM COMPROU\nAPROVOU", 230, 130, TINTA, 0.2, 0.8),
      ...elemento("v-comentario", { x: 90, y: 640, w: 900, h: 230 }, 0.6, ["menina-presente"]),
      ...elemento("v-comentario", { x: 90, y: 920, w: 900, h: 230 }, 1.0, ["festa-mesa"]),
      ...elemento("v-estrelas", { x: 140, y: 1260, w: 800, h: 287 }, 1.3),
    ]),
    3.2,
    "parado",
    { tipo: "circulo", duracao: 0.55 },
  );
  /* 4. O preço que cai e o botão com o clique. */
  const c4 = cena(
    lima([
      ...elemento("v-preco", { x: 90, y: 380, w: 900, h: 473 }, 0.1),
      ...elemento("v-botao-comprar", { x: 160, y: 1050, w: 760, h: 420 }, 1.2),
      ...anima(t("Estoque", { x: 140, y: 1560, w: 800, h: 90 }, { texto: "só 12 no estoque", fonte: "caveat", peso: 700, tamanho: 84, cor: TINTA, alinhamento: "centro" }), "desfocar", 2.1, { duracao: 0.4 }),
    ]),
    3.4,
    "parado",
    { tipo: "persianas", duracao: 0.6 },
  );
  /* 5. Contagem e o fim com a marca. */
  const c5 = cena(
    escuro([
      ...elemento("v-contagem", { x: 290, y: 330, w: 500, h: 500 }, 0),
      ...anima(t("Corre", { x: 40, y: 920, w: 1000, h: 420 }, { texto: "CORRE QUE\nACABA", fonte: "anton", peso: 400, tamanho: 190, linha: 1.02, cor: LIMA, alinhamento: "centro" }), "elastico", 2.3, { duracao: 0.6 }),
      ...anima(marca(p, 330, 1460, 110, "#FFFFFF", "centro"), "surgir", 2.6),
      ...elemento("v-ao-vivo", { x: 360, y: 1640, w: 360, h: 112 }, 2.7),
      ...elemento("v-confete", { x: 0, y: 0, w: W, h: 1080 }, 2.3),
    ]),
    4.2,
    "parado",
    { tipo: "preto", duracao: 0.5 },
  );
  void nada;
  const paginas = [c1, c2, c3, c4, c5];
  const { inicios } = temposDoProjeto(paginas);
  return comTrilha(paginas, "fma-loyalty-freak-music-dance-robot-activate", [
    ["pop", inicios[1] + 1.0, 0.55],
    ["clique", inicios[3] + 2.0, 0.7],
    ["carimbo", inicios[3] + 1.1, 0.5],
    ["tambor", inicios[4] + 0.2, 0.5],
    ["impacto", inicios[4] + 2.3, 0.6],
  ]);
}

export const MODELOS_EM_RITMO = [
  { chave: "animado-reels-em-ritmo", nome: "Reels em ritmo", descricao: "Legenda palavra por palavra, marca-texto, círculos e cursor que clica, com trilha", montar: reelsEmRitmo, feitoPara: "story" as const, paginas: 8, animado: true },
  { chave: "animado-produto-em-movimento", nome: "Produto em movimento", descricao: "A foto viaja com quadros-chave, o celular atravessa, comentários, preço e contagem", montar: produtoEmMovimento, feitoPara: "story" as const, paginas: 5, animado: true },
];
void ic;
export { anima, cena, fala, marcado, escuro, papel, lima, fotoNaMoldura, elemento, manchas, comTrilha };
