/*
 * Os blocos novos da página inicial (oferta relâmpago, cupom em destaque,
 * corte de produção, próxima live, listas automáticas, kits, vistos
 * recentemente, vídeo, texto com imagem, perguntas frequentes, onde estamos,
 * faixa que desliza, imagem com pontos, antes e depois) pelo assistente:
 * criar_secao e alterar_secao recebem o campo `bloco` com os ajustes de cada
 * tipo, e ver_estrutura_da_loja mostra o que cada bloco tem.
 *
 * A configuração gravada é a mesma do editor do painel
 * (components/pagina-inicial/ajustes-dos-widgets.tsx) e a loja lê com
 * apps/public/src/lib/widgets-da-home.ts.
 */

import type { Campos } from "../esquema";
import {
  LIMITE_DA_FAIXA,
  LIMITE_DA_OFERTA,
  LIMITE_DE_PERGUNTAS,
  LIMITE_DE_PONTOS,
  MODOS_DOS_WIDGETS,
  NOME_DO_WIDGET,
  PROPORCOES_DO_WIDGET,
  ehWidget,
  instanteDeBrasilia,
  linkDeVideoValido,
  problemaDoWidget,
  textoDeBrasilia,
  widgetNovo,
  type ModoDoWidget,
} from "../../widgets-da-home.ts";

export { MODOS_DOS_WIDGETS, ehWidget, widgetNovo, problemaDoWidget, type ModoDoWidget };

/* O nome que o assistente usa para cada bloco (minúsculo, como os outros tipos). */
export const NOME_DO_BLOCO: Record<ModoDoWidget, string> = Object.fromEntries(
  MODOS_DOS_WIDGETS.map((modo) => [modo, NOME_DO_WIDGET[modo].toLowerCase()])
) as Record<ModoDoWidget, string>;


const PROPORCOES = Object.keys(PROPORCOES_DO_WIDGET);

/** O campo `bloco` de criar_secao e alterar_secao. */
export const CAMPO_DO_BLOCO: Campos = {
  bloco: {
    tipo: "objeto",
    descricao:
      "Os ajustes dos blocos novos (oferta relâmpago, cupom em destaque, peça até tal hora, próxima live, lista automática, kits e combos, vistos recentemente, vídeo, texto com imagem, perguntas frequentes, onde estamos, faixa que desliza, imagem com pontos, antes e depois). Mande só os campos do tipo; na alteração, só o que muda (listas mandadas substituem as de hoje).",
    campos: {
      produtos: { tipo: "textos", descricao: `Oferta relâmpago: ids dos produtos (de buscar_produtos), até ${LIMITE_DA_OFERTA}.`, maxItens: LIMITE_DA_OFERTA, max: 40 },
      termina_em: { tipo: "texto", descricao: 'Oferta relâmpago: quando termina, na hora de Brasília, "AAAA-MM-DD HH:MM". O bloco some sozinho depois.', max: 20 },
      selo: { tipo: "texto", descricao: "Oferta relâmpago: o selo (ex.: Só hoje).", max: 30 },
      subtitulo: { tipo: "texto", descricao: "Oferta relâmpago: frase de apoio.", max: 140 },
      cupom: { tipo: "texto", descricao: "Cupom em destaque: o código ou o id do cupom (de listar_cupons).", max: 60 },
      chamada: { tipo: "texto", descricao: "Cupom em destaque: a frase ao lado do código. Texto com imagem: a linha pequena em cima do título.", max: 120 },
      mostrar_validade: { tipo: "booleano", descricao: "Cupom em destaque: mostra até quando vale (padrão sim)." },
      frase: { tipo: "texto", descricao: "Peça até tal hora: a frase antes do corte (vazio usa a padrão).", max: 120 },
      destaque: { tipo: "texto", descricao: "Peça até tal hora: a linha de baixo.", max: 60 },
      mostrar_sem_live: { tipo: "booleano", descricao: "Próxima live: mostra um recado quando não há live marcada (padrão não: o bloco some)." },
      mensagem_sem_live: { tipo: "texto", descricao: "Próxima live: o recado sem live.", max: 140 },
      lista: { tipo: "opcao", descricao: "Lista automática: lancamentos (os mais novos) ou mais_vendidos (vendas de verdade, sem cancelados). Não existe lista de promoção (os produtos não têm preço promocional).", opcoes: ["lancamentos", "mais_vendidos"] },
      dias: { tipo: "inteiro", descricao: "Lista de mais vendidos: o período (7, 30 ou 90 dias).", min: 7, max: 90 },
      quantidade: { tipo: "inteiro", descricao: "Lista automática, kits e vistos recentemente: quantos mostrar.", min: 1, max: 12 },
      combos: { tipo: "textos", descricao: "Kits e combos: os ids dos combos (compre junto) na ordem. Vazio mostra todos.", maxItens: 24, max: 40 },
      video: { tipo: "texto", descricao: "Vídeo: o link do YouTube ou do Vimeo.", max: 500 },
      lado: { tipo: "opcao", descricao: 'Vídeo: "inteiro" (texto embaixo), "esquerda" (vídeo à esquerda) ou "direita". Texto com imagem: lado da imagem ("esquerda" ou "direita").', opcoes: ["inteiro", "esquerda", "direita"] },
      texto: { tipo: "texto", descricao: "Vídeo e texto com imagem: o texto. Aceita **negrito**, [texto](link) e linhas com \"- \" para lista.", max: 2000 },
      imagem: { tipo: "texto", descricao: "Texto com imagem e imagem com pontos: a imagem (link https, \"envio:...\" ou anexo).", max: 2000 },
      capa: { tipo: "texto", descricao: "Vídeo: a capa (opcional).", max: 2000 },
      proporcao: { tipo: "opcao", descricao: "Texto com imagem, imagem com pontos e antes e depois: formato da imagem (faixa = 16:9, não vale para texto com imagem).", opcoes: PROPORCOES },
      botao_texto: { tipo: "texto", descricao: "Texto com imagem: texto do botão (vazio tira).", max: 30 },
      botao_link: { tipo: "texto", descricao: "Texto com imagem: para onde o botão leva (caminho ou endereço da própria loja).", max: 500 },
      botao_produto: { tipo: "texto", descricao: "Texto com imagem: o botão leva a este produto (id).", max: 40 },
      perguntas: {
        tipo: "lista",
        descricao: `Perguntas frequentes: até ${LIMITE_DE_PERGUNTAS}, na ordem. Vão também para o Google.`,
        maxItens: LIMITE_DE_PERGUNTAS,
        itens: {
          pergunta: { tipo: "texto", descricao: "A pergunta.", obrigatorio: true, max: 200 },
          resposta: { tipo: "texto", descricao: "A resposta (aceita **negrito** e links).", obrigatorio: true, max: 1500 },
        },
      },
      abrir_primeira: { tipo: "booleano", descricao: "Perguntas frequentes: a primeira já aberta." },
      usar_endereco_da_loja: { tipo: "booleano", descricao: "Onde estamos: usa o endereço da empresa (padrão sim)." },
      endereco: { tipo: "texto", descricao: "Onde estamos: o endereço escrito, uma linha por parte (quando não usa o da empresa).", max: 300 },
      horarios: { tipo: "texto", descricao: "Onde estamos: os horários, um por linha.", max: 400 },
      mostrar_mapa: { tipo: "booleano", descricao: "Onde estamos: mostra o mapa (padrão sim)." },
      mostrar_retiradas: { tipo: "booleano", descricao: "Onde estamos: lista os pontos de retirada." },
      frases: { tipo: "textos", descricao: `Faixa que desliza: as frases (até ${LIMITE_DA_FAIXA}).`, maxItens: LIMITE_DA_FAIXA, max: 80 },
      velocidade: { tipo: "opcao", descricao: "Faixa que desliza: a velocidade.", opcoes: ["devagar", "normal", "rapido"] },
      tamanho: { tipo: "opcao", descricao: "Faixa que desliza: tamanho da letra.", opcoes: ["p", "m", "g"] },
      cor_de_fundo: { tipo: "texto", descricao: "Faixa que desliza: cor do fundo (#hex). Vazio usa a cor da loja.", max: 9 },
      cor_do_texto: { tipo: "texto", descricao: "Faixa que desliza: cor das letras (#hex).", max: 9 },
      pontos: {
        tipo: "lista",
        descricao: `Imagem com pontos: até ${LIMITE_DE_PONTOS} pontos, cada um em x e y (porcentagem da imagem, 0 a 100, a partir do canto de cima à esquerda) e o produto que ele mostra.`,
        maxItens: LIMITE_DE_PONTOS,
        itens: {
          x: { tipo: "numero", descricao: "De 0 (esquerda) a 100 (direita).", obrigatorio: true, min: 0, max: 100 },
          y: { tipo: "numero", descricao: "De 0 (em cima) a 100 (embaixo).", obrigatorio: true, min: 0, max: 100 },
          produto: { tipo: "texto", descricao: "O id do produto.", obrigatorio: true, max: 40 },
        },
      },
      antes: { tipo: "texto", descricao: "Antes e depois: a imagem de antes.", max: 2000 },
      depois: { tipo: "texto", descricao: "Antes e depois: a imagem de depois.", max: 2000 },
      rotulo_antes: { tipo: "texto", descricao: "Antes e depois: o nome da foto de antes.", max: 24 },
      rotulo_depois: { tipo: "texto", descricao: "Antes e depois: o nome da foto de depois.", max: 24 },
      inicio: { tipo: "inteiro", descricao: "Antes e depois: onde a barra começa (5 a 95).", min: 5, max: 95 },
      margem: { tipo: "opcao", descricao: "Espaço em cima e embaixo do bloco.", opcoes: ["nenhum", "pequeno", "normal"] },
    },
  },
};

/* Os campos de cada tipo (o resto é recusado, para o assistente não achar que mudou algo). */
const CAMPOS_DE_CADA: Record<ModoDoWidget, string[]> = {
  flash_offer: ["produtos", "termina_em", "selo", "subtitulo", "margem"],
  coupon_highlight: ["cupom", "chamada", "mostrar_validade", "margem"],
  order_cutoff: ["frase", "destaque", "margem"],
  next_live: ["mostrar_sem_live", "mensagem_sem_live", "margem"],
  auto_list: ["lista", "dias", "quantidade", "margem"],
  combo_highlight: ["combos", "quantidade", "margem"],
  recently_viewed: ["quantidade", "margem"],
  video_section: ["video", "lado", "texto", "capa", "margem"],
  text_image: ["chamada", "texto", "imagem", "lado", "proporcao", "botao_texto", "botao_link", "botao_produto", "margem"],
  faq: ["perguntas", "abrir_primeira", "margem"],
  store_location: ["usar_endereco_da_loja", "endereco", "horarios", "mostrar_mapa", "mostrar_retiradas", "margem"],
  marquee: ["frases", "velocidade", "tamanho", "cor_de_fundo", "cor_do_texto"],
  shoppable_image: ["imagem", "proporcao", "pontos", "margem"],
  before_after: ["antes", "depois", "rotulo_antes", "rotulo_depois", "proporcao", "inicio", "margem"],
};

/* Os campos de imagem: o assistente prepara e sobe; a configuração guarda o endereço. */
export const CAMPOS_DE_IMAGEM: Record<string, string> = { imagem: "imagem", capa: "capa", antes: "antes", depois: "depois" };

/** A medida da imagem de cada campo, para recortar no formato do bloco. */
export function medidaDaImagem(modo: ModoDoWidget, campo: string, config: Record<string, unknown>) {
  if (campo === "capa") return { largura: 1280, altura: 720 };
  const chave = String(config.proporcao || (modo === "text_image" ? "quadrado" : "paisagem"));
  const medida = PROPORCOES_DO_WIDGET[chave as keyof typeof PROPORCOES_DO_WIDGET] ?? PROPORCOES_DO_WIDGET.paisagem;
  return { largura: medida.largura, altura: medida.altura };
}

export type PedidoDoBloco = {
  config: Record<string, unknown>;
  mudancas: string[];
  /* O que ainda precisa ser conferido no banco ou preparado. */
  produtos: string[];
  cupom: string | null;
  combos: string[] | null;
  imagens: Array<{ campo: string; valor: unknown }>;
  linkDoBotao: string | null;
  produtoDoBotao: string | null;
};

const SO_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

/**
 * Junta o pedido do assistente na configuração do bloco (sem banco). Erro de
 * formato volta como texto em `erro`; ids e imagens ficam listados para quem
 * chama conferir e preparar.
 */
export function aplicarPedidoDoBloco(
  modo: ModoDoWidget,
  configAtual: Record<string, unknown>,
  pedido: Record<string, unknown> | undefined
): { ok: true; pedido: PedidoDoBloco } | { ok: false; erro: string } {
  const config: Record<string, unknown> = { ...configAtual, mode: modo };
  const resultado: PedidoDoBloco = { config, mudancas: [], produtos: [], cupom: null, combos: null, imagens: [], linkDoBotao: null, produtoDoBotao: null };
  if (!pedido) return { ok: true, pedido: resultado };
  const permitidos = CAMPOS_DE_CADA[modo];
  for (const campo of Object.keys(pedido)) {
    if (pedido[campo] === undefined) continue;
    if (!permitidos.includes(campo)) return { ok: false, erro: `"bloco.${campo}" não vale para ${NOME_DO_BLOCO[modo]}. Campos deste tipo: ${permitidos.join(", ")}.` };
  }
  const marcar = (nome: string) => resultado.mudancas.push(nome);
  const texto = (campo: string) => String(pedido[campo] ?? "").trim();

  for (const campo of ["selo", "subtitulo", "chamada", "frase", "destaque", "mensagem_sem_live", "texto", "horarios", "endereco", "rotulo_antes", "rotulo_depois", "botao_texto"]) {
    if (pedido[campo] === undefined) continue;
    config[campo] = String(pedido[campo]).replace(/\r\n?/g, "\n").trim();
    marcar(campo.replace(/_/g, " "));
  }
  for (const campo of ["mostrar_validade", "mostrar_sem_live", "abrir_primeira", "usar_endereco_da_loja", "mostrar_mapa", "mostrar_retiradas"]) {
    if (pedido[campo] === undefined) continue;
    config[campo] = pedido[campo] === true;
    marcar(campo.replace(/_/g, " "));
  }
  for (const campo of ["velocidade", "tamanho", "margem", "lista"]) {
    if (pedido[campo] === undefined) continue;
    config[campo] = pedido[campo];
    marcar(campo);
  }
  if (pedido.lado !== undefined) {
    if (modo === "text_image" && pedido.lado === "inteiro") return { ok: false, erro: 'No texto com imagem, "lado" é "esquerda" ou "direita".' };
    config.lado = pedido.lado;
    marcar("lado");
  }
  if (pedido.proporcao !== undefined) {
    if (modo === "text_image" && pedido.proporcao === "faixa") return { ok: false, erro: 'No texto com imagem, a proporção é quadrado, retrato ou paisagem.' };
    config.proporcao = pedido.proporcao;
    marcar("formato");
  }
  if (pedido.dias !== undefined) {
    const dias = Number(pedido.dias);
    if (![7, 30, 90].includes(dias)) return { ok: false, erro: "dias: use 7, 30 ou 90." };
    config.dias = dias;
    marcar("período");
  }
  if (pedido.quantidade !== undefined) {
    config.quantidade = Math.trunc(Number(pedido.quantidade));
    marcar("quantidade");
  }
  if (pedido.inicio !== undefined) {
    config.inicio = Math.trunc(Number(pedido.inicio));
    marcar("início da barra");
  }
  if (pedido.termina_em !== undefined) {
    const lido = texto("termina_em").replace(" ", "T");
    const instante = instanteDeBrasilia(lido);
    if (instante === null) return { ok: false, erro: 'termina_em: use "AAAA-MM-DD HH:MM" (hora de Brasília).' };
    config.termina_em = textoDeBrasilia(instante);
    marcar("fim da oferta");
  }
  if (pedido.produtos !== undefined) {
    const ids = (pedido.produtos as unknown[]).map((v) => String(v).trim().toLowerCase()).filter(Boolean);
    const ruim = ids.find((id) => !SO_UUID.test(id));
    if (ruim) return { ok: false, erro: `"${ruim}" não é um id de produto. Use os ids de buscar_produtos.` };
    config.produtos = [...new Set(ids)].slice(0, LIMITE_DA_OFERTA);
    resultado.produtos.push(...(config.produtos as string[]));
    marcar("produtos");
  }
  if (pedido.cupom !== undefined) {
    resultado.cupom = texto("cupom");
    marcar("cupom");
  }
  if (pedido.combos !== undefined) {
    const ids = (pedido.combos as unknown[]).map((v) => String(v).trim().toLowerCase()).filter(Boolean);
    const ruim = ids.find((id) => !SO_UUID.test(id));
    if (ruim) return { ok: false, erro: `"${ruim}" não é um id de combo.` };
    resultado.combos = [...new Set(ids)];
    config.combos = resultado.combos;
    marcar("kits");
  }
  if (pedido.video !== undefined) {
    if (!linkDeVideoValido(pedido.video)) return { ok: false, erro: "video: mande o link de um vídeo do YouTube ou do Vimeo." };
    config.url = texto("video");
    marcar("vídeo");
  }
  if (pedido.perguntas !== undefined) {
    config.itens = (pedido.perguntas as Array<Record<string, unknown>>)
      .map((p) => ({ pergunta: String(p.pergunta ?? "").trim(), resposta: String(p.resposta ?? "").trim() }))
      .filter((p) => p.pergunta && p.resposta)
      .slice(0, LIMITE_DE_PERGUNTAS);
    marcar("perguntas");
  }
  if (pedido.frases !== undefined) {
    config.itens = (pedido.frases as unknown[]).map((f) => String(f ?? "").trim()).filter(Boolean).slice(0, LIMITE_DA_FAIXA);
    marcar("frases");
  }
  for (const campo of ["cor_de_fundo", "cor_do_texto"]) {
    if (pedido[campo] === undefined) continue;
    const cor = texto(campo);
    if (cor && !/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(cor)) return { ok: false, erro: `${campo}: use hexadecimal, como #1a73e8.` };
    config[campo] = cor;
    marcar(campo.replace(/_/g, " "));
  }
  if (pedido.pontos !== undefined) {
    const pontos = (pedido.pontos as Array<Record<string, unknown>>).map((p) => ({
      x: Math.min(100, Math.max(0, Math.round(Number(p.x) * 10) / 10)),
      y: Math.min(100, Math.max(0, Math.round(Number(p.y) * 10) / 10)),
      produto_id: String(p.produto ?? "").trim().toLowerCase(),
    }));
    const ruim = pontos.find((p) => !SO_UUID.test(p.produto_id) || !Number.isFinite(p.x) || !Number.isFinite(p.y));
    if (ruim) return { ok: false, erro: "pontos: cada ponto precisa de x e y (0 a 100) e do id do produto." };
    config.pontos = pontos.slice(0, LIMITE_DE_PONTOS);
    resultado.produtos.push(...pontos.map((p) => p.produto_id));
    marcar("pontos");
  }
  if (pedido.botao_link !== undefined && pedido.botao_produto !== undefined) return { ok: false, erro: "O botão leva a um lugar só: botao_link ou botao_produto." };
  if (pedido.botao_link !== undefined) {
    resultado.linkDoBotao = texto("botao_link");
    marcar("link do botão");
  }
  if (pedido.botao_produto !== undefined) {
    const id = texto("botao_produto").toLowerCase();
    if (!SO_UUID.test(id)) return { ok: false, erro: "botao_produto: mande o id do produto." };
    resultado.produtoDoBotao = id;
    config.botao_link = { tipo: "produto", produto_id: id };
    resultado.produtos.push(id);
    marcar("link do botão");
  }
  for (const campo of Object.keys(CAMPOS_DE_IMAGEM)) {
    if (pedido[campo] === undefined || !String(pedido[campo]).trim()) continue;
    resultado.imagens.push({ campo, valor: pedido[campo] });
    marcar(campo);
  }
  return { ok: true, pedido: resultado };
}

/** O bloco em palavras, para ver_estrutura_da_loja e as prévias. */
export function blocoEmPalavras(modo: ModoDoWidget, config: Record<string, unknown>, nomeDoProduto: (id: string) => string) {
  const c = config;
  switch (modo) {
    case "flash_offer":
      return {
        termina_em: c.termina_em ? `${String(c.termina_em).replace("T", " ")} (Brasília)` : "sem data: não aparece",
        produtos: (Array.isArray(c.produtos) ? (c.produtos as string[]) : []).map((id) => ({ id, nome: nomeDoProduto(id) })),
        selo: c.selo || "Oferta",
        observacao: "Mostra o preço de verdade (os produtos não têm preço promocional) com o selo e o relógio; some quando termina.",
      };
    case "coupon_highlight":
      return { cupom_id: c.cupom_id || null, frase: c.chamada || null, mostrar_validade: c.mostrar_validade !== false };
    case "order_cutoff":
      return { observacao: "Usa o corte de produção da loja (alterar_corte_de_producao). Desligado, o bloco não aparece.", frase: c.frase || null };
    case "next_live":
      return { observacao: "A próxima live de venda marcada na vitrine; some sem live.", recado_sem_live: c.mostrar_sem_live === true ? c.mensagem_sem_live || "padrão" : null };
    case "auto_list":
      return { lista: c.lista === "mais_vendidos" ? `mais vendidos (${Number(c.dias) || 30} dias, vendas de verdade)` : "lançamentos", quantidade: Number(c.quantidade) || 8 };
    case "combo_highlight":
      return { kits: Array.isArray(c.combos) && c.combos.length ? c.combos : "todos os compre junto", quantidade: Number(c.quantidade) || 6 };
    case "recently_viewed":
      return { quantidade: Number(c.quantidade) || 8, observacao: "Guardado só no navegador de cada visitante." };
    case "video_section":
      return { video: c.url || null, lado: c.lado || "inteiro", texto: c.texto || null };
    case "text_image":
      return { imagem: c.imagem || null, chamada: c.chamada || null, texto: c.texto || null, lado: c.lado || "esquerda", botao: c.botao_texto ? { texto: c.botao_texto, link: c.botao_link ?? null } : null };
    case "faq":
      return { perguntas: Array.isArray(c.itens) ? c.itens : [] };
    case "store_location":
      return { endereco: c.usar_endereco_da_loja === false ? c.endereco || null : "o da empresa", horarios: c.horarios || null, mapa: c.mostrar_mapa !== false, retiradas: c.mostrar_retiradas === true };
    case "marquee":
      return { frases: Array.isArray(c.itens) ? c.itens : [], velocidade: c.velocidade || "normal" };
    case "shoppable_image":
      return {
        imagem: c.imagem || null,
        pontos: (Array.isArray(c.pontos) ? (c.pontos as Array<Record<string, unknown>>) : []).map((p) => ({ x: p.x, y: p.y, produto: nomeDoProduto(String(p.produto_id || "")) })),
      };
    case "before_after":
      return { antes: c.antes || null, depois: c.depois || null, rotulos: [c.rotulo_antes || "Antes", c.rotulo_depois || "Depois"] };
  }
}

/** O que falta para o bloco aparecer (as imagens que vão subir contam como postas). */
export function faltaNoBloco(modo: ModoDoWidget, config: Record<string, unknown>, imagensPendentes: string[]) {
  const comImagens = { ...config };
  for (const campo of imagensPendentes) comImagens[CAMPOS_DE_IMAGEM[campo]] = "pendente";
  return problemaDoWidget(modo, comImagens);
}
