/*
  A página inicial da loja: os tipos de seção, a leitura da configuração de
  cada uma e o protocolo do editor que o painel abre por cima da loja.

  Regras puras, sem "@/": o teste roda sozinho (pagina-inicial.test.ts). O
  painel tem a cópia das mesmas regras em apps/admin/src/lib/pagina-inicial.ts
  (os dois apps não dividem pacote); mudou aqui, muda lá.

  Os tipos antigos (produtos escolhidos, todos os produtos, categoria,
  subcategoria, banner de imagem e atalhos de categorias) continuam
  desenhados exatamente como antes. Os novos só aparecem em loja que montou
  a página pelo editor:

  - image_grid         imagens lado a lado, cada uma com o seu link;
  - banner_carousel    carrossel de banners dentro da página;
  - featured_products  os primeiros produtos da loja ("Mais vendidos" do Vendor);
  - category_circles   as categorias principais em círculos, sozinhas.

  O banner de imagem ganhou `formato` (faixa, largo, panorâmico, cinema),
  link para produto ou categoria e texto por cima. Banner sem `formato` é o
  banner de sempre, com o desenho de sempre.
*/

import { destinoDoLink } from "./destino-do-link.ts";
import { MODOS_DOS_WIDGETS, lancamentos, lerListaAutomatica, produtosNaOrdem } from "./widgets-da-home.ts";

export const MODOS_ANTIGOS = [
  "manual_products",
  "all_products",
  "category_products",
  "subcategory_products",
  "image_banner",
  "category_navigator",
] as const;

export const MODOS_NOVOS = [
  "image_grid",
  "banner_carousel",
  "featured_products",
  "category_circles",
  /* Os blocos novos (oferta, cupom, vídeo, perguntas...): lib/widgets-da-home.ts. */
  ...MODOS_DOS_WIDGETS,
] as const;

export const MODOS_DA_HOME = [...MODOS_ANTIGOS, ...MODOS_NOVOS] as const;

export type ModoAntigo = (typeof MODOS_ANTIGOS)[number];
export type ModoNovo = (typeof MODOS_NOVOS)[number];
export type ModoDaHome = (typeof MODOS_DA_HOME)[number];

export function ehModoDaHome(valor: unknown): valor is ModoDaHome {
  return typeof valor === "string" && (MODOS_DA_HOME as readonly string[]).includes(valor);
}

export function configDaSecao(config: unknown): Record<string, unknown> {
  if (config && typeof config === "object" && !Array.isArray(config)) return config as Record<string, unknown>;
  if (typeof config === "string") {
    try {
      const lido = JSON.parse(config);
      if (lido && typeof lido === "object" && !Array.isArray(lido)) return lido as Record<string, unknown>;
    } catch {
      return {};
    }
  }
  return {};
}

/** O tipo da seção como a loja sempre leu: config.mode manda, e banner antigo sem modo é banner. */
export function modoDaSecao(secao: { config?: unknown; section_type?: unknown; type?: unknown }): ModoDaHome {
  const config = configDaSecao(secao.config);
  const doConfig = config.mode;
  const temImagem = Boolean(config.image_url || config.imageUrl);
  if (temImagem && !doConfig) return "image_banner";
  const bruto = doConfig ?? secao.section_type ?? secao.type;
  return ehModoDaHome(bruto) ? bruto : "manual_products";
}

/* ------------------------------------------------------------ medidas */

export type FormatoDeBanner = "faixa" | "largo" | "panoramico" | "cinema";

export const FORMATOS_DE_BANNER: Record<FormatoDeBanner, { proporcao: number; largura: number; altura: number }> = {
  faixa: { proporcao: 8, largura: 1920, altura: 240 },
  largo: { proporcao: 16 / 5, largura: 1920, altura: 600 },
  panoramico: { proporcao: 32 / 9, largura: 1920, altura: 540 },
  cinema: { proporcao: 21 / 9, largura: 1920, altura: 823 },
};

export type ProporcaoDaImagem = "quadrado" | "retrato" | "paisagem" | "faixa";

export const PROPORCOES_DA_IMAGEM: Record<ProporcaoDaImagem, { proporcao: number; largura: number; altura: number }> = {
  quadrado: { proporcao: 1, largura: 1200, altura: 1200 },
  retrato: { proporcao: 4 / 5, largura: 960, altura: 1200 },
  paisagem: { proporcao: 8 / 5, largura: 1440, altura: 900 },
  faixa: { proporcao: 3, largura: 1800, altura: 600 },
};

/* A foto do banner no celular, quando a loja manda uma separada. */
export const PROPORCAO_DO_CELULAR = { proporcao: 4 / 3, largura: 1080, altura: 810 };

function escolha<T extends string>(valor: unknown, opcoes: readonly T[], padrao: T): T {
  return typeof valor === "string" && (opcoes as readonly string[]).includes(valor) ? (valor as T) : padrao;
}

function inteiro(valor: unknown, minimo: number, maximo: number, padrao: number) {
  const numero = Math.trunc(Number(valor));
  if (!Number.isFinite(numero)) return padrao;
  return Math.min(maximo, Math.max(minimo, numero));
}

function texto(valor: unknown, maximo: number) {
  return String(valor ?? "").replace(/\s+/g, " ").trim().slice(0, maximo);
}

/** Endereço de imagem que pode ir para um src: http(s) ou caminho do próprio site. */
export function urlDeImagem(valor: unknown): string {
  const bruto = String(valor ?? "").trim();
  if (!bruto || bruto.length > 2000) return "";
  if (/^https?:\/\//i.test(bruto)) return bruto;
  if (bruto.startsWith("/") && !bruto.startsWith("//")) return bruto;
  return "";
}

/* ------------------------------------------------------------- link */

export type TipoDeLink = "nenhum" | "produto" | "categoria" | "url";

export type LinkDaSecao = {
  tipo: TipoDeLink;
  produto_id?: string;
  categoria_id?: string;
  categoria_slug?: string;
  url?: string;
  nova_aba?: boolean;
};

export function lerLink(valor: unknown): LinkDaSecao {
  const bruto = valor && typeof valor === "object" ? (valor as Record<string, unknown>) : {};
  const tipo = escolha<TipoDeLink>(bruto.tipo, ["nenhum", "produto", "categoria", "url"], "nenhum");
  const link: LinkDaSecao = { tipo, nova_aba: bruto.nova_aba === true };
  if (tipo === "produto") link.produto_id = texto(bruto.produto_id, 80);
  if (tipo === "categoria") {
    link.categoria_id = texto(bruto.categoria_id, 80);
    link.categoria_slug = texto(bruto.categoria_slug, 200);
  }
  if (tipo === "url") link.url = texto(bruto.url, 500);
  return link;
}

/**
 * Para onde o clique leva. Produto e categoria são páginas da própria loja;
 * o endereço digitado passa pela mesma regra do banner principal
 * (destinoDoLink: sem https:// vira https://, javascript: não vira link).
 */
export function destinoDaSecao(
  link: LinkDaSecao,
  slug: string,
  categorias?: Array<{ id: string; slug?: string | null }>
): { href: string; externo: boolean; novaAba: boolean } | null {
  const novaAba = link.nova_aba === true;
  if (link.tipo === "produto" && link.produto_id) {
    return { href: `/${slug}/p/${encodeURIComponent(link.produto_id)}`, externo: false, novaAba };
  }
  if (link.tipo === "categoria") {
    const daLista = link.categoria_id ? categorias?.find((c) => c.id === link.categoria_id) : null;
    const caminho = daLista?.slug || link.categoria_slug || "";
    if (!caminho) return null;
    return { href: `/${slug}/c/${encodeURIComponent(caminho)}`, externo: false, novaAba };
  }
  if (link.tipo === "url") {
    const destino = destinoDoLink(link.url);
    return destino ? { ...destino, novaAba } : null;
  }
  return null;
}

/* ----------------------------------------------------------- imagens */

export type ImagemDaSecao = {
  imagem: string;
  imagem_celular: string;
  legenda: string;
  link: LinkDaSecao;
};

export function lerImagens(valor: unknown, maximo: number): ImagemDaSecao[] {
  if (!Array.isArray(valor)) return [];
  return valor
    .filter((item) => item && typeof item === "object")
    .slice(0, maximo)
    .map((item) => {
      const bruto = item as Record<string, unknown>;
      return {
        imagem: urlDeImagem(bruto.imagem),
        imagem_celular: urlDeImagem(bruto.imagem_celular),
        legenda: texto(bruto.legenda, 80),
        link: lerLink(bruto.link),
      };
    });
}

/* ---------------------------------------------- imagens lado a lado */

export type LayoutDaGrade = "2" | "3" | "4" | "1+2" | "2+1";
export type EspacoDaGrade = "nenhum" | "pequeno" | "medio";
export type CantosDaGrade = "retos" | "suaves" | "redondos";
export type GradeNoCelular = "empilhar" | "lado_a_lado" | "deslizar";
export type EspacoVertical = "nenhum" | "pequeno" | "normal";

export const QUANTAS_NA_GRADE: Record<LayoutDaGrade, number> = { "2": 2, "3": 3, "4": 4, "1+2": 3, "2+1": 3 };

export type GradeDeImagens = {
  layout: LayoutDaGrade;
  proporcao: ProporcaoDaImagem;
  espaco: EspacoDaGrade;
  cantos: CantosDaGrade;
  celular: GradeNoCelular;
  margem: EspacoVertical;
  mostrarTitulo: boolean;
  itens: ImagemDaSecao[];
};

export function lerGrade(config: Record<string, unknown>): GradeDeImagens {
  const layout = escolha<LayoutDaGrade>(config.layout, ["2", "3", "4", "1+2", "2+1"], "3");
  return {
    layout,
    proporcao: escolha<ProporcaoDaImagem>(config.proporcao, ["quadrado", "retrato", "paisagem", "faixa"], "quadrado"),
    espaco: escolha<EspacoDaGrade>(config.espaco, ["nenhum", "pequeno", "medio"], "medio"),
    cantos: escolha<CantosDaGrade>(config.cantos, ["retos", "suaves", "redondos"], "suaves"),
    celular: escolha<GradeNoCelular>(config.celular, ["empilhar", "lado_a_lado", "deslizar"], "deslizar"),
    margem: escolha<EspacoVertical>(config.margem, ["nenhum", "pequeno", "normal"], "normal"),
    mostrarTitulo: config.mostrar_titulo === true,
    itens: lerImagens(config.itens, QUANTAS_NA_GRADE[layout]),
  };
}

/* ------------------------------------------------- banner e carrossel */

export type AlinhamentoDoTexto = "esquerda" | "centro";
export type TomDoTexto = "claro" | "escuro";

export type BannerDaSecao = {
  formato: FormatoDeBanner | null;
  imagem: string;
  imagemCelular: string;
  link: LinkDaSecao;
  titulo: string;
  subtitulo: string;
  botao: string;
  alinhamento: AlinhamentoDoTexto;
  tom: TomDoTexto;
  margem: EspacoVertical;
};

export function lerBanner(config: Record<string, unknown>): BannerDaSecao {
  const formato = typeof config.formato === "string" && config.formato in FORMATOS_DE_BANNER ? (config.formato as FormatoDeBanner) : null;
  /* O banner antigo guardava só link_url e open_in_new_tab. */
  const link = config.link
    ? lerLink(config.link)
    : config.link_url
      ? lerLink({ tipo: "url", url: config.link_url, nova_aba: config.open_in_new_tab === true })
      : lerLink(null);
  return {
    formato,
    imagem: urlDeImagem(config.image_url ?? config.imageUrl),
    imagemCelular: urlDeImagem(config.imagem_celular),
    link,
    titulo: texto(config.titulo_na_imagem, 90),
    subtitulo: texto(config.subtitulo, 160),
    botao: texto(config.botao, 30),
    alinhamento: escolha<AlinhamentoDoTexto>(config.alinhamento_do_texto, ["esquerda", "centro"], "esquerda"),
    tom: escolha<TomDoTexto>(config.tom_do_texto, ["claro", "escuro"], "claro"),
    margem: escolha<EspacoVertical>(config.margem, ["nenhum", "pequeno", "normal"], "normal"),
  };
}

export type CarrosselDaSecao = {
  formato: FormatoDeBanner;
  automatico: boolean;
  intervalo: number;
  margem: EspacoVertical;
  mostrarTitulo: boolean;
  itens: ImagemDaSecao[];
};

export const LIMITE_DO_CARROSSEL = 8;

export function lerCarrossel(config: Record<string, unknown>): CarrosselDaSecao {
  return {
    formato: typeof config.formato === "string" && config.formato in FORMATOS_DE_BANNER ? (config.formato as FormatoDeBanner) : "largo",
    automatico: config.automatico !== false,
    intervalo: inteiro(config.intervalo, 3, 15, 6),
    margem: escolha<EspacoVertical>(config.margem, ["nenhum", "pequeno", "normal"], "normal"),
    mostrarTitulo: config.mostrar_titulo === true,
    itens: lerImagens(config.itens, LIMITE_DO_CARROSSEL),
  };
}

/* --------------------------------------------- produtos e categorias */

export function quantidadeDeProdutos(config: Record<string, unknown>, padrao: number) {
  return inteiro(config.quantidade, 1, 48, padrao);
}

type ProdutoComFoto = {
  id: string;
  category_id?: string | null;
  image_url?: string | null;
  product_images?: Array<{ image_url?: string | null; is_primary?: boolean | null }> | null;
};

type CategoriaDaLoja = { id: string; name: string; slug?: string | null; parent_id?: string | null };

/** A foto que representa a categoria: a do primeiro produto dela, como o Vendor sempre fez. */
export function fotosDasCategorias(produtos: ProdutoComFoto[]): Map<string, string> {
  const mapa = new Map<string, string>();
  for (const produto of produtos) {
    const categoria = produto.category_id;
    if (!categoria || mapa.has(categoria)) continue;
    const imagens = (produto.product_images || []).filter((img) => img?.image_url);
    const principal = imagens.find((img) => img.is_primary) || imagens[0];
    const foto = principal?.image_url || produto.image_url || null;
    if (foto) mapa.set(categoria, foto);
  }
  return mapa;
}

/**
 * Os tipos novos que os temas Padrão e Vitrine desenham com o desenho de um
 * tipo antigo. "Os primeiros produtos" é uma seção de produtos escolhidos
 * com a lista pronta; "as categorias em círculos" é a navegação por
 * categoria com as principais; categoria "só dela" é a regra da
 * subcategoria (sem as de dentro). O resto passa intacto.
 */
export function secoesParaOsTemasAntigos<
  S extends { section_type?: unknown; config?: unknown; products?: unknown[] }
>(
  secoes: S[],
  produtos: Array<ProdutoComFoto & Record<string, unknown>>,
  categorias: CategoriaDaLoja[],
  /* Os mais vendidos de verdade (ids, por período), para as listas automáticas. */
  extras?: { maisVendidos?: Partial<Record<string, string[]>> }
): S[] {
  return secoes.map((secao) => {
    const modo = modoDaSecao(secao);
    const config = configDaSecao(secao.config);
    /* Lista automática com produto vira seção de produtos com os cards de
       sempre da loja; sem produto fica com o tipo dela (some na loja). */
    if (modo === "auto_list") {
      const lista = lerListaAutomatica(config);
      const escolhidos =
        lista.lista === "mais_vendidos"
          ? produtosNaOrdem(extras?.maisVendidos?.[String(lista.dias)] ?? [], produtos as Array<{ id: string } & Record<string, unknown>>).slice(0, lista.quantidade)
          : lancamentos(produtos, lista.quantidade);
      if (!escolhidos.length) return secao;
      return {
        ...secao,
        section_type: "manual_products",
        config: { ...config, mode: "manual_products", show_view_more: false },
        products: escolhidos,
      };
    }
    if (modo === "featured_products") {
      return {
        ...secao,
        section_type: "manual_products",
        config: { ...config, mode: "manual_products", show_view_more: false },
        products: produtos.slice(0, quantidadeDeProdutos(config, 4)),
      };
    }
    if (modo === "category_circles") {
      const fotos = fotosDasCategorias(produtos);
      const limite = inteiro(config.limite, 1, 12, 12);
      const itens = categorias
        .filter((c) => !c.parent_id)
        .slice(0, limite)
        .map((c) => ({ target_category_id: c.id, target_category_slug: c.slug || c.id, label: "", image_url: fotos.get(c.id) || "" }));
      return { ...secao, section_type: "manual_products", config: { ...config, mode: "category_navigator", items: itens } };
    }
    if (modo === "category_products" && config.so_da_categoria === true) {
      return { ...secao, config: { ...config, mode: "subcategory_products" } };
    }
    return secao;
  });
}

/* ------------------------------------------------ a home do Vendor */

/** A seção como o editor e o banco a guardam (sem os produtos montados). */
export type SecaoDaPagina = {
  id: string;
  title: string;
  background_color: string;
  text_color: string;
  layout_width: "full" | "contained";
  title_align: "left" | "center" | "right";
  title_size: "sm" | "md" | "lg" | "xl";
  section_type: "manual_products" | "all_products";
  config: Record<string, unknown>;
  produtos: string[];
};

type BannerDaLoja = {
  id: string;
  image_url: string;
  link_type?: string | null;
  product_id?: string | null;
  link_url?: string | null;
  open_in_new_tab?: boolean | null;
};

function linkDoBannerDaLoja(banner: BannerDaLoja): LinkDaSecao {
  if (banner.link_type === "product" && banner.product_id) {
    return { tipo: "produto", produto_id: banner.product_id, nova_aba: banner.open_in_new_tab === true };
  }
  if (banner.link_type === "url" && banner.link_url) {
    return { tipo: "url", url: banner.link_url, nova_aba: banner.open_in_new_tab === true };
  }
  return { tipo: "nenhum" };
}

function secaoBase(id: string, title: string, config: Record<string, unknown>): SecaoDaPagina {
  return {
    id,
    title,
    background_color: "#ffffff",
    text_color: "#111827",
    layout_width: "full",
    title_align: "center",
    title_size: "lg",
    section_type: "manual_products",
    config,
    produtos: [],
  };
}

/**
 * A home automática do Vendor escrita como seções: é o rascunho que o editor
 * abre numa loja Vendor que nunca publicou, para nada mudar na tela até a
 * pessoa mexer. Segue VendorHome linha a linha: os círculos das categorias
 * principais, "Mais vendidos" (os 4 primeiros), até três categorias
 * principais com dois produtos ou mais (só os produtos da própria
 * categoria), e o banner duplo (2º e 3º banners) depois da segunda grade.
 * Sem nenhuma grade, "Nossos produtos" com 8.
 *
 * A Boutique é da família Vendor, mas tem a home automática dela
 * (arranjoAutomaticoDaBoutique, logo abaixo).
 */
export function arranjoAutomaticoDoVendor(
  produtos: ProdutoComFoto[],
  categorias: CategoriaDaLoja[],
  banners: BannerDaLoja[]
): SecaoDaPagina[] {
  const raizes = categorias.filter((c) => !c.parent_id);
  const grades: SecaoDaPagina[] = [];
  if (produtos.length) {
    grades.push(secaoBase("auto:mais-vendidos", "Mais vendidos", { mode: "featured_products", quantidade: 4 }));
  }
  for (const categoria of raizes) {
    const doGrupo = produtos.filter((p) => p.category_id === categoria.id);
    if (doGrupo.length < 2) continue;
    grades.push(
      secaoBase(`auto:categoria:${categoria.id}`, categoria.name, {
        mode: "category_products",
        target_category_id: categoria.id,
        target_category_slug: categoria.slug || categoria.id,
        so_da_categoria: true,
        quantidade: 4,
        show_view_more: true,
      })
    );
    if (grades.length >= 4) break;
  }

  const lista: SecaoDaPagina[] = [];
  if (raizes.length) {
    lista.push(secaoBase("auto:categorias", "Categorias", { mode: "category_circles" }));
  }
  const doMeio = banners.filter((b) => b && b.image_url).slice(1, 3);
  grades.forEach((grade, indice) => {
    lista.push(grade);
    if (indice === 1 && doMeio.length >= 2) {
      lista.push(
        secaoBase("auto:banner-duplo", "Banner duplo", {
          mode: "image_grid",
          layout: "2",
          proporcao: "paisagem",
          espaco: "medio",
          cantos: "redondos",
          celular: "deslizar",
          margem: "pequeno",
          itens: doMeio.map((banner) => ({ imagem: banner.image_url, link: linkDoBannerDaLoja(banner) })),
        })
      );
    }
  });
  if (!grades.length && produtos.length) {
    lista.push(secaoBase("auto:nossos-produtos", "Nossos produtos", { mode: "featured_products", quantidade: 8 }));
  }
  return lista;
}

/* ------------------------------------------------ a home da Boutique */

type ProdutoDaBoutique = ProdutoComFoto & {
  product_kind?: string | null;
  product_images?: Array<{ image_url?: string | null; is_primary?: boolean | null; sort_order?: number | null }> | null;
};

/** A foto que representa a categoria nos cards grandes da Boutique: a 2ª
 *  foto (a da modelo vestindo, em loja de moda) do primeiro produto que tiver
 *  uma; senão, a principal. A mesma conta de fotoDaCategoria do template. */
export function fotoDaCategoriaDaBoutique(produtos: ProdutoDaBoutique[]): string | null {
  for (const produto of produtos) {
    const imagens = [...(produto.product_images || [])]
      .filter((img) => img?.image_url)
      .sort((a, b) => Number(a.sort_order ?? 0) - Number(b.sort_order ?? 0));
    const principal = imagens.find((img) => img.is_primary) || imagens[0];
    const capa = principal?.image_url || produto.image_url || null;
    const segunda = imagens.find((img) => img.image_url !== capa)?.image_url;
    if (segunda) return segunda;
  }
  for (const produto of produtos) {
    const imagens = (produto.product_images || []).filter((img) => img?.image_url);
    const principal = imagens.find((img) => img.is_primary) || imagens[0];
    const foto = principal?.image_url || produto.image_url || null;
    if (foto) return foto;
  }
  return null;
}

/**
 * A home automática da Boutique escrita como seções, na ordem da referência
 * (depois do topo fixo: banner, benefícios, stories e "compre pelo vídeo"):
 *
 *   1. TOP SELLERS (featured_products: na Boutique é a grade centralizada com
 *      abas das categorias principais);
 *   2. os dois cards grandes de categoria (category_navigator com as duas
 *      categorias principais que têm foto);
 *   3. Lançamentos em carrossel (auto_list, os cadastrados por último);
 *   4. Mais vendidos em carrossel quando a loja tem vendas (auto_list de 90
 *      dias); sem venda, a vitrine da maior categoria;
 *   5. "Elas vestem" (combo_highlight: os kits do "Compre junto" viram looks)
 *      só quando há kit com os dois produtos à mostra.
 *
 * A loja Boutique que nunca publicou mostra exatamente isto (BoutiqueHome
 * desenha estas seções), e o editor abre com este mesmo rascunho.
 */
export function arranjoAutomaticoDaBoutique(
  produtos: ProdutoDaBoutique[],
  categorias: CategoriaDaLoja[],
  extras?: {
    nomeDaLoja?: string | null;
    /* Os ids dos mais vendidos (90 dias), na ordem da venda. */
    maisVendidos?: string[] | null;
    /* Os kits do "Compre junto" (ativos). */
    combos?: Array<{ main_product_id: string; combo_product_id: string }> | null;
  }
): SecaoDaPagina[] {
  const lista: SecaoDaPagina[] = [];
  if (!produtos.length) return lista;
  const nome = String(extras?.nomeDaLoja || "").trim();
  const visiveis = new Set(produtos.map((p) => String(p.id)));

  /* As categorias principais com os produtos delas e das filhas, da que tem
     mais produto para a que tem menos. */
  const paiDe = new Map(categorias.map((c) => [c.id, c.parent_id || null]));
  const raizDe = (id: string | null | undefined) => {
    let atual = id || null;
    for (let i = 0; atual && i < 6; i += 1) {
      const pai = paiDe.get(atual);
      if (!pai) return atual;
      atual = pai;
    }
    return atual;
  };
  const porRaiz = new Map<string, ProdutoDaBoutique[]>();
  for (const produto of produtos) {
    const raiz = raizDe(produto.category_id);
    if (!raiz) continue;
    const doGrupo = porRaiz.get(raiz) || [];
    doGrupo.push(produto);
    porRaiz.set(raiz, doGrupo);
  }
  const principais = categorias
    .filter((c) => !c.parent_id)
    .map((categoria) => ({ categoria, produtos: porRaiz.get(categoria.id) || [] }))
    .filter((item) => item.produtos.length > 0)
    .sort((a, b) => b.produtos.length - a.produtos.length);

  lista.push(secaoBase("auto:top-sellers", "Top sellers", { mode: "featured_products", quantidade: 4 }));

  const cards = principais
    .map(({ categoria, produtos: doGrupo }) => ({ categoria, foto: fotoDaCategoriaDaBoutique(doGrupo) }))
    .filter((item): item is { categoria: CategoriaDaLoja; foto: string } => Boolean(item.foto))
    .slice(0, 2);
  if (cards.length) {
    lista.push(
      secaoBase("auto:categorias", "Coleções", {
        mode: "category_navigator",
        items: cards.map(({ categoria, foto }) => ({
          target_category_id: categoria.id,
          target_category_slug: categoria.slug || categoria.id,
          label: categoria.name,
          image_url: foto,
        })),
      })
    );
  }

  if (produtos.length > 4) {
    lista.push(secaoBase("auto:lancamentos", "Lançamentos", { mode: "auto_list", lista: "lancamentos", quantidade: 12, margem: "nenhum" }));
  }

  const vendidos = (extras?.maisVendidos || []).filter((id) => visiveis.has(String(id)));
  if (vendidos.length >= 2) {
    lista.push(
      secaoBase("auto:mais-vendidos", "Mais vendidos", { mode: "auto_list", lista: "mais_vendidos", dias: 90, quantidade: 12, margem: "nenhum" })
    );
  } else {
    /* Sem venda ainda: a maior categoria em carrossel, com o "ver tudo". */
    const maior = principais.find((item) => item.produtos.length >= 2);
    if (maior) {
      lista.push(
        secaoBase(`auto:categoria:${maior.categoria.id}`, maior.categoria.name, {
          mode: "category_products",
          target_category_id: maior.categoria.id,
          target_category_slug: maior.categoria.slug || maior.categoria.id,
          quantidade: 12,
          show_view_more: true,
        })
      );
    }
  }

  const temKit = (extras?.combos || []).some(
    (combo) => visiveis.has(String(combo.main_product_id)) && visiveis.has(String(combo.combo_product_id))
  );
  if (temKit) {
    lista.push(
      secaoBase("auto:elas-vestem", nome ? `Elas vestem ${nome}` : "Elas vestem", { mode: "combo_highlight", quantidade: 12, margem: "nenhum" })
    );
  }
  return lista;
}

/* As seções que a home automática da Boutique precisa sondar no banco antes
   de se montar (mais vendidos de 90 dias e os kits): o servidor lê os dados
   delas e só então decide o arranjo. */
export const SONDAGEM_DA_HOME_DA_BOUTIQUE: Array<{ config: Record<string, unknown> }> = [
  { config: { mode: "auto_list", lista: "mais_vendidos", dias: 90 } },
  { config: { mode: "combo_highlight" } },
];

/* ------------------------------------------------ protocolo do editor */

export const CANAL_DO_EDITOR = "ktalogu:editor-da-loja";

/** A seção que o painel manda para a loja desenhar no rascunho. */
export type SecaoDoRascunho = SecaoDaPagina & {
  rotulo: string;
  /* A agendada fora da janela, vista no editor como se estivesse no ar
     (só a prévia; nunca vai para o banco). */
  previa_no_ar?: boolean;
};

export type MensagemDoPainel =
  | { canal: typeof CANAL_DO_EDITOR; tipo: "rascunho"; secoes: SecaoDoRascunho[]; selecionada: string | null }
  | { canal: typeof CANAL_DO_EDITOR; tipo: "rolar"; id: string }
  | { canal: typeof CANAL_DO_EDITOR; tipo: "selecionar"; id: string | null }
  /* Quanto o painel encolheu a loja para caber: os botões do editor crescem
     na mesma medida, para continuarem do tamanho de um dedo. */
  | { canal: typeof CANAL_DO_EDITOR; tipo: "escala"; valor: number };

export type AcaoDaLoja = "selecionar" | "subir" | "descer" | "editar" | "duplicar" | "ocultar" | "excluir" | "inserir";

export type MensagemDaLoja =
  | {
      canal: typeof CANAL_DO_EDITOR;
      tipo: "pronto";
      tema: string;
      homeEditada: boolean;
      arranjoAutomatico: SecaoDaPagina[] | null;
    }
  | { canal: typeof CANAL_DO_EDITOR; tipo: "acao"; acao: AcaoDaLoja; id: string | null; indice?: number }
  | { canal: typeof CANAL_DO_EDITOR; tipo: "link-bloqueado" };

const ACOES: readonly AcaoDaLoja[] = ["selecionar", "subir", "descer", "editar", "duplicar", "ocultar", "excluir", "inserir"];

function secaoDoRascunho(valor: unknown): SecaoDoRascunho | null {
  if (!valor || typeof valor !== "object") return null;
  const s = valor as Record<string, unknown>;
  const id = texto(s.id, 120);
  if (!id) return null;
  const config = configDaSecao(s.config);
  return {
    id,
    rotulo: texto(s.rotulo, 60),
    title: String(s.title ?? "").slice(0, 200),
    background_color: texto(s.background_color, 20) || "#ffffff",
    text_color: texto(s.text_color, 20) || "#111827",
    layout_width: s.layout_width === "contained" ? "contained" : "full",
    title_align: escolha(s.title_align, ["left", "center", "right"] as const, "center"),
    title_size: escolha(s.title_size, ["sm", "md", "lg", "xl"] as const, "lg"),
    section_type: s.section_type === "all_products" ? "all_products" : "manual_products",
    config,
    produtos: Array.isArray(s.produtos) ? s.produtos.map((p) => texto(p, 80)).filter(Boolean).slice(0, 120) : [],
    ...(s.previa_no_ar === true ? { previa_no_ar: true } : {}),
  };
}

/** Confere a mensagem que chegou do painel. Qualquer coisa fora do formato vira null. */
export function lerMensagemDoPainel(dados: unknown): MensagemDoPainel | null {
  if (!dados || typeof dados !== "object") return null;
  const m = dados as Record<string, unknown>;
  if (m.canal !== CANAL_DO_EDITOR) return null;
  if (m.tipo === "rascunho" && Array.isArray(m.secoes)) {
    const secoes = m.secoes.slice(0, 60).map(secaoDoRascunho).filter((s): s is SecaoDoRascunho => Boolean(s));
    return { canal: CANAL_DO_EDITOR, tipo: "rascunho", secoes, selecionada: typeof m.selecionada === "string" ? m.selecionada : null };
  }
  if (m.tipo === "rolar" && typeof m.id === "string") return { canal: CANAL_DO_EDITOR, tipo: "rolar", id: m.id };
  if (m.tipo === "selecionar") return { canal: CANAL_DO_EDITOR, tipo: "selecionar", id: typeof m.id === "string" ? m.id : null };
  if (m.tipo === "escala" && Number.isFinite(Number(m.valor))) {
    return { canal: CANAL_DO_EDITOR, tipo: "escala", valor: Math.min(1, Math.max(0.2, Number(m.valor))) };
  }
  return null;
}

/** Confere a mensagem que chegou da loja (o painel usa a cópia desta regra). */
export function lerMensagemDaLoja(dados: unknown): MensagemDaLoja | null {
  if (!dados || typeof dados !== "object") return null;
  const m = dados as Record<string, unknown>;
  if (m.canal !== CANAL_DO_EDITOR) return null;
  if (m.tipo === "acao" && typeof m.acao === "string" && (ACOES as readonly string[]).includes(m.acao)) {
    return {
      canal: CANAL_DO_EDITOR,
      tipo: "acao",
      acao: m.acao as AcaoDaLoja,
      id: typeof m.id === "string" ? m.id : null,
      ...(Number.isInteger(m.indice) ? { indice: m.indice as number } : {}),
    };
  }
  if (m.tipo === "link-bloqueado") return { canal: CANAL_DO_EDITOR, tipo: "link-bloqueado" };
  return null;
}
