/*
  O editor da página inicial da loja, do lado do painel: os tipos de seção,
  o rascunho, o que vai para o banco e a conversa com a loja.

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

  Como a página fica no banco (nada mudou de lugar):
  - catalog_sections: uma linha por seção. O tipo de verdade mora em
    config.mode; section_type guarda só "all_products" ou "manual_products";
  - catalog_section_products: os produtos das seções de produtos escolhidos;
  - catalog_home_publicacoes (migration 196): o "antes" de cada publicação.
  Quem grava é a função salvar_pagina_inicial, numa transação só.
*/

import { MODOS_DOS_WIDGETS, NOME_DO_WIDGET, RESUMO_DO_WIDGET, ehWidget, problemaDoWidget, widgetNovo } from "./widgets-da-home.ts";
import { problemaDaExibicao } from "./exibicao-da-secao.ts";

export const MODOS = [
  "manual_products",
  "all_products",
  "category_products",
  "subcategory_products",
  "image_banner",
  "category_navigator",
  "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 type Modo = (typeof MODOS)[number];

export type Tema = "default" | "vitrine" | "vendor" | "boutique";

export function temaDaLoja(valor: unknown): Tema {
  const texto = String(valor ?? "").trim().toLowerCase();
  if (texto === "vendor") return "vendor";
  if (texto === "boutique") return "boutique";
  if (texto === "vitrine") return "vitrine";
  return "default";
}

/** O nome do modelo como a pessoa vê no painel. */
export const NOME_DO_TEMA: Record<Tema, string> = {
  default: "Padrão",
  vitrine: "Vitrine",
  vendor: "Vendor",
  boutique: "Ateliê",
};

/**
 * A família Vendor (Vendor e Boutique): sem grade fixa, sem cor, largura ou
 * título por seção (o desenho do tema manda) e com a página automática até a
 * primeira publicação pelo editor. Toda decisão "faz como o Vendor" do editor
 * passa por aqui, para um modelo novo da família não ficar de fora de nenhuma.
 */
export function ehFamiliaVendor(tema: unknown): boolean {
  const t = temaDaLoja(tema);
  return t === "vendor" || t === "boutique";
}

export const NOME_DO_MODO: Record<Modo, string> = {
  manual_products: "Produtos escolhidos",
  all_products: "Todos os produtos",
  category_products: "Produtos de uma categoria",
  subcategory_products: "Produtos de uma subcategoria",
  image_banner: "Banner",
  category_navigator: "Atalhos de categorias",
  image_grid: "Imagens lado a lado",
  banner_carousel: "Carrossel de banners",
  featured_products: "Primeiros produtos",
  category_circles: "Categorias em círculos",
  ...NOME_DO_WIDGET,
};

export const RESUMO_DO_MODO: Record<Modo, string> = {
  manual_products: "Você escolhe os produtos e a ordem.",
  all_products: "A grade com todos os produtos da loja.",
  category_products: "Os produtos de uma categoria, sozinhos.",
  subcategory_products: "Os produtos de uma subcategoria.",
  image_banner: "Uma imagem grande, com link e texto.",
  category_navigator: "Fotos redondas que levam às categorias.",
  image_grid: "2, 3 ou 4 imagens, cada uma com o seu link.",
  banner_carousel: "Vários banners passando, com link em cada.",
  featured_products: "Os primeiros produtos da ordem da loja.",
  category_circles: "As categorias principais, sozinhas.",
  ...RESUMO_DO_WIDGET,
};

export function ehModo(valor: unknown): valor is Modo {
  return typeof valor === "string" && (MODOS as readonly string[]).includes(valor);
}

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

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

export const FORMATOS_DE_BANNER: Record<FormatoDeBanner, { nome: string; medida: string; proporcao: number; largura: number; altura: number }> = {
  faixa: { nome: "Faixa fina", medida: "8:1", proporcao: 8, largura: 1920, altura: 240 },
  largo: { nome: "Largo", medida: "16:5", proporcao: 16 / 5, largura: 1920, altura: 600 },
  panoramico: { nome: "Panorâmico", medida: "32:9", proporcao: 32 / 9, largura: 1920, altura: 540 },
  cinema: { nome: "Cinema", medida: "21:9", proporcao: 21 / 9, largura: 1920, altura: 823 },
};

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

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

export const PROPORCAO_DO_CELULAR = { medida: "4:3", proporcao: 4 / 3, largura: 1080, altura: 810 };

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

export const LIMITE_DO_CARROSSEL = 8;
export const LIMITE_DE_ATALHOS = 12;
export const LIMITE_DE_PRODUTOS = 60;
export const LIMITE_DE_SECOES = 40;

/* ------------------------------------------------------------- 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 = (["nenhum", "produto", "categoria", "url"] as const).includes(bruto.tipo as TipoDeLink) ? (bruto.tipo as TipoDeLink) : "nenhum";
  const link: LinkDaSecao = { tipo, nova_aba: bruto.nova_aba === true };
  if (tipo === "produto") link.produto_id = String(bruto.produto_id ?? "");
  if (tipo === "categoria") {
    link.categoria_id = String(bruto.categoria_id ?? "");
    link.categoria_slug = String(bruto.categoria_slug ?? "");
  }
  if (tipo === "url") link.url = String(bruto.url ?? "");
  return link;
}

/**
 * O endereço digitado no link: caminho da própria loja ("/aurora/c/x") ou
 * endereço completo com https. Devolve a mensagem do problema, ou null.
 */
export function problemaDoEndereco(url: unknown): string | null {
  const texto = String(url ?? "").trim();
  if (!texto) return "Cole o endereço do link.";
  if (texto.length > 500) return "O endereço passou de 500 letras.";
  if (/^\/(?!\/)\S*$/.test(texto)) return null;
  if (/^(javascript|data|vbscript):/i.test(texto)) return "Esse endereço não pode virar link.";
  if (/^https:\/\/[^\s/]+\.[^\s]+$/i.test(texto)) return null;
  if (/^http:\/\//i.test(texto)) return "Use o endereço com https://.";
  if (/^[a-z0-9-]+(\.[a-z0-9-]+)+([/?#]\S*)?$/i.test(texto)) return null;
  return "Endereço inválido. Use https://... ou um caminho da loja, como /minha-loja/c/canecas.";
}

/** O link está completo para gravar? (produto escolhido, categoria escolhida, endereço válido) */
export function linkIncompleto(link: LinkDaSecao): string | null {
  if (link.tipo === "produto" && !link.produto_id) return "Escolha o produto do link.";
  if (link.tipo === "categoria" && !link.categoria_id && !link.categoria_slug) return "Escolha a categoria do link.";
  if (link.tipo === "url") return problemaDoEndereco(link.url);
  return null;
}

/* --------------------------------------------------------- a seção */

export type SecaoDoEditor = {
  /* A chave da seção no editor: o id do banco, ou "nova:..." para a que
     ainda não foi publicada. É também o id que a loja usa no rascunho. */
  chave: string;
  id: string | null;
  modo: Modo;
  title: string;
  background_color: string;
  text_color: string;
  layout_width: "full" | "contained";
  title_align: "left" | "center" | "right";
  title_size: "sm" | "md" | "lg" | "xl";
  config: Record<string, unknown>;
  produtos: string[];
  ativa: boolean;
  /* A grade "Todos os produtos" que os temas Padrão e Vitrine põem sozinhos
     no fim quando a loja não tem a seção. Só vira linha no banco se a pessoa
     mexer nela (mudar de lugar ou de título). */
  virtual?: boolean;
};

/* O mesmo id da grade inventada pela loja (StoreContent), para a moldura
   do editor cair nela. */
export const CHAVE_DA_GRADE_INVENTADA = "__synthetic_all_products__";

const PREFIXO_ANTIGO = "[[KTCFG:";
const SUFIXO_ANTIGO = "]]";

function metadadosAntigos(tituloBruto: unknown): { titulo: string; config: Record<string, unknown> } {
  const titulo = String(tituloBruto ?? "");
  const inicio = titulo.indexOf(PREFIXO_ANTIGO);
  if (inicio === -1) return { titulo, config: {} };
  const fim = titulo.indexOf(SUFIXO_ANTIGO, inicio + PREFIXO_ANTIGO.length);
  if (fim === -1) return { titulo, config: {} };
  const limpo = `${titulo.slice(0, inicio)}${titulo.slice(fim + SUFIXO_ANTIGO.length)}`.trim();
  try {
    const lido = JSON.parse(decodeURIComponent(titulo.slice(inicio + PREFIXO_ANTIGO.length, fim)));
    if (lido && typeof lido === "object" && !Array.isArray(lido)) return { titulo: limpo, config: lido as Record<string, unknown> };
  } catch {
    /* configuração estragada: fica só o título */
  }
  return { titulo: limpo, config: {} };
}

export function configDaColuna(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 {};
}

export type LinhaDaSecao = {
  id: string;
  title?: string | null;
  config?: unknown;
  section_type?: string | null;
  type?: string | null;
  position?: number | null;
  active?: boolean | null;
  layout_width?: string | null;
  title_align?: string | null;
  title_size?: string | null;
  background_color?: string | null;
  text_color?: string | null;
};

/** A linha do banco como o editor trabalha (título limpo, configuração juntada, modo). */
export function secaoDaLinha(linha: LinhaDaSecao, produtos: string[]): SecaoDoEditor {
  const antigo = metadadosAntigos(linha.title);
  const config = { ...antigo.config, ...configDaColuna(linha.config) };
  const temImagem = Boolean(config.image_url || config.imageUrl);
  const bruto = !config.mode && temImagem ? "image_banner" : (config.mode ?? linha.section_type ?? linha.type);
  const modo: Modo = ehModo(bruto) ? bruto : "manual_products";
  return {
    chave: linha.id,
    id: linha.id,
    modo,
    title: antigo.titulo,
    background_color: linha.background_color || "#ffffff",
    text_color: linha.text_color || "#000000",
    layout_width: linha.layout_width === "contained" ? "contained" : "full",
    title_align: linha.title_align === "left" || linha.title_align === "right" ? linha.title_align : "center",
    title_size: linha.title_size === "sm" || linha.title_size === "md" || linha.title_size === "xl" ? linha.title_size : "lg",
    config: { ...config, mode: modo },
    produtos: modo === "manual_products" ? produtos : [],
    ativa: linha.active !== false,
  };
}

/** A grade que a loja inventa no fim, igual ao que o StoreContent desenha. */
export function gradeInventada(): SecaoDoEditor {
  return {
    chave: CHAVE_DA_GRADE_INVENTADA,
    id: null,
    modo: "all_products",
    title: "Todos os Produtos",
    background_color: "#ffffff",
    text_color: "#111827",
    layout_width: "full",
    title_align: "left",
    title_size: "md",
    config: { mode: "all_products", show_view_more: false },
    produtos: [],
    ativa: true,
    virtual: true,
  };
}

/**
 * O rascunho que o editor abre.
 * - Padrão e Vitrine: as seções como estão (as escondidas também, marcadas),
 *   e a grade inventada no fim quando a loja não tem "Todos os produtos".
 * - Família Vendor (Vendor e Boutique) que já publicou pelo editor: as seções
 *   como estão.
 * - Família Vendor que nunca publicou: a home automática escrita como seções
 *   (vem da própria loja) e, escondidas no fim, as seções antigas que ela
 *   nunca mostrou, para nada se perder.
 */
export function rascunhoInicial(
  tema: Tema,
  secoes: SecaoDoEditor[],
  vendor: { homeEditada: boolean; arranjoAutomatico: SecaoDoEditor[] | null }
): SecaoDoEditor[] {
  if (ehFamiliaVendor(tema)) {
    if (vendor.homeEditada || !vendor.arranjoAutomatico) return secoes;
    return [...vendor.arranjoAutomatico, ...secoes.map((s) => ({ ...s, ativa: false }))];
  }
  const temGrade = secoes.some((s) => s.modo === "all_products" && s.ativa);
  return temGrade ? secoes : [...secoes, gradeInventada()];
}

/** A seção que a loja mandou (home automática do Vendor) no formato do editor. */
export function secaoDoArranjo(valor: unknown, indice: number): SecaoDoEditor | null {
  if (!valor || typeof valor !== "object") return null;
  const s = valor as Record<string, unknown>;
  const config = configDaColuna(s.config);
  const modo = ehModo(config.mode) ? config.mode : null;
  if (!modo) return null;
  return {
    chave: `nova:auto-${indice}-${String(s.id || "").replace(/[^a-z0-9-]/gi, "").slice(0, 40)}`,
    id: null,
    modo,
    title: String(s.title ?? "").slice(0, 120),
    background_color: "#ffffff",
    text_color: "#111827",
    layout_width: s.layout_width === "contained" ? "contained" : "full",
    title_align: "center",
    title_size: "lg",
    config: { ...config, mode: modo },
    produtos: [],
    ativa: true,
  };
}

let contador = 0;
export function chaveNova() {
  contador += 1;
  return `nova:${Date.now().toString(36)}${contador.toString(36)}${Math.random().toString(36).slice(2, 6)}`;
}

/** A seção nova de cada tipo, com o que a tela sugere de começo. */
export function secaoNova(modo: Modo, extra?: { categoria?: { id: string; slug: string; name: string } | null }): SecaoDoEditor {
  const base: SecaoDoEditor = {
    chave: chaveNova(),
    id: null,
    modo,
    title: NOME_DO_MODO[modo],
    background_color: "#ffffff",
    text_color: "#111827",
    layout_width: "full",
    title_align: "left",
    title_size: "lg",
    config: { mode: modo },
    produtos: [],
    ativa: true,
  };
  switch (modo) {
    case "manual_products":
      return { ...base, title: "Destaques", config: { mode: modo, show_view_more: true } };
    case "all_products":
      return { ...base, title: "Todos os Produtos", title_size: "md", config: { mode: modo, show_view_more: false } };
    case "category_products":
    case "subcategory_products":
      return {
        ...base,
        title: extra?.categoria?.name || NOME_DO_MODO[modo],
        config: {
          mode: modo,
          show_view_more: true,
          ...(extra?.categoria ? { target_category_id: extra.categoria.id, target_category_slug: extra.categoria.slug } : {}),
        },
      };
    case "image_banner":
      return { ...base, title: "Banner", layout_width: "contained", config: { mode: modo, formato: "largo", image_url: "", link: { tipo: "nenhum" }, margem: "normal" } };
    case "category_navigator":
      return { ...base, title: "Navegue por categoria", layout_width: "contained", config: { mode: modo, items: [] } };
    case "image_grid":
      return {
        ...base,
        title: "Imagens lado a lado",
        layout_width: "contained",
        config: { mode: modo, layout: "3", proporcao: "quadrado", espaco: "medio", cantos: "suaves", celular: "deslizar", margem: "normal", itens: [] },
      };
    case "banner_carousel":
      return { ...base, title: "Carrossel de banners", layout_width: "contained", config: { mode: modo, formato: "largo", automatico: true, intervalo: 6, margem: "normal", itens: [] } };
    case "featured_products":
      return { ...base, title: "Mais vendidos", title_align: "center", config: { mode: modo, quantidade: 4 } };
    case "category_circles":
      return { ...base, title: "Categorias", config: { mode: modo } };
    default: {
      /* Os blocos novos (lib/widgets-da-home.ts). */
      const novo = widgetNovo(modo);
      return { ...base, ...novo };
    }
  }
}

/** Uma cópia da seção, como nova (a lista de produtos e as imagens vão junto). */
export function duplicada(secao: SecaoDoEditor): SecaoDoEditor {
  return {
    ...JSON.parse(JSON.stringify(secao)),
    chave: chaveNova(),
    id: null,
    virtual: false,
    title: secao.title ? `${secao.title} (cópia)` : secao.title,
  };
}

/* -------------------------------------------------- regras da página */

/** Os temas Padrão e Vitrine sempre têm a grade de produtos; ela não sai nem some. */
export function ehGradeFixa(tema: Tema, secao: SecaoDoEditor) {
  return !ehFamiliaVendor(tema) && secao.modo === "all_products";
}

/** Só uma grade "Todos os produtos" na página. */
export function podeAdicionar(modo: Modo, secoes: SecaoDoEditor[]): string | null {
  if (modo === "all_products" && secoes.some((s) => s.modo === "all_products" && s.ativa)) {
    return 'A página já tem "Todos os produtos".';
  }
  if (secoes.length >= LIMITE_DE_SECOES) return `A página aceita até ${LIMITE_DE_SECOES} seções.`;
  return null;
}

export function mover<T>(lista: T[], de: number, para: number): T[] {
  if (de === para || de < 0 || para < 0 || de >= lista.length || para >= lista.length) return lista;
  const nova = [...lista];
  const [item] = nova.splice(de, 1);
  nova.splice(para, 0, item);
  return nova;
}

/**
 * Sobe ou desce uma seção pulando as escondidas (o que a pessoa vê na loja
 * é a ordem das ligadas).
 */
export function moverNaPagina(secoes: SecaoDoEditor[], chave: string, direcao: -1 | 1): SecaoDoEditor[] {
  const indice = secoes.findIndex((s) => s.chave === chave);
  if (indice < 0) return secoes;
  let alvo = indice + direcao;
  while (alvo >= 0 && alvo < secoes.length && !secoes[alvo].ativa) alvo += direcao;
  if (alvo < 0 || alvo >= secoes.length) return secoes;
  return mover(secoes, indice, alvo);
}

/** Onde entra uma seção nova pedida pelo "+" da loja (índice entre as ligadas). */
export function indiceNaLista(secoes: SecaoDoEditor[], indiceEntreAsLigadas: number): number {
  const ligadas = secoes.map((s, i) => ({ s, i })).filter(({ s }) => s.ativa);
  if (indiceEntreAsLigadas <= 0) return ligadas.length ? ligadas[0].i : 0;
  if (indiceEntreAsLigadas >= ligadas.length) return ligadas.length ? ligadas[ligadas.length - 1].i + 1 : secoes.length;
  return ligadas[indiceEntreAsLigadas].i;
}

/* ------------------------------------------------- problemas a resolver */

export type Problema = { chave: string; texto: string };

/** O que impede publicar (link incompleto, categoria não escolhida). */
export function problemasDaPagina(secoes: SecaoDoEditor[]): Problema[] {
  const problemas: Problema[] = [];
  for (const secao of secoes) {
    if (!secao.ativa) continue;
    const c = secao.config;
    const nome = secao.title || NOME_DO_MODO[secao.modo];
    /* Agendar, aparelho e fundo valem para qualquer tipo (lib/exibicao-da-secao.ts). */
    const daExibicao = problemaDaExibicao(c);
    if (daExibicao) problemas.push({ chave: secao.chave, texto: `"${nome}": ${daExibicao}` });
    if ((secao.modo === "category_products" || secao.modo === "subcategory_products") && !c.target_category_id) {
      problemas.push({ chave: secao.chave, texto: `"${nome}": escolha a categoria.` });
    }
    if (secao.modo === "image_banner") {
      if (c.link) {
        const erro = linkIncompleto(lerLink(c.link));
        if (erro) problemas.push({ chave: secao.chave, texto: `"${nome}": ${erro}` });
      }
    }
    if (ehWidget(secao.modo)) {
      const erro = problemaDoWidget(secao.modo, c);
      if (erro) problemas.push({ chave: secao.chave, texto: `"${nome}": ${erro}` });
      const botao = secao.modo === "text_image" && c.botao_texto && c.botao_link ? linkIncompleto(lerLink(c.botao_link)) : null;
      if (botao) problemas.push({ chave: secao.chave, texto: `"${nome}", botão: ${botao}` });
    }
    if (secao.modo === "image_grid" || secao.modo === "banner_carousel") {
      const itens = Array.isArray(c.itens) ? (c.itens as Array<Record<string, unknown>>) : [];
      itens.forEach((item, i) => {
        if (!item) return;
        const erro = linkIncompleto(lerLink(item.link));
        if (erro) problemas.push({ chave: secao.chave, texto: `"${nome}", imagem ${i + 1}: ${erro}` });
      });
    }
  }
  return problemas;
}

/* --------------------------------------------------- o que vai ao banco */

export type SecaoParaGravar = {
  id: string | null;
  title: string;
  background_color: string;
  text_color: string;
  placement: "above" | "below";
  layout_width: "full" | "contained";
  title_align: "left" | "center" | "right";
  title_size: "sm" | "md" | "lg" | "xl";
  section_type: "all_products" | "manual_products";
  config: Record<string, unknown>;
  active: boolean;
  produtos?: string[];
};

function mesmaGradeInventada(secao: SecaoDoEditor) {
  const padrao = gradeInventada();
  return (
    secao.title === padrao.title &&
    secao.background_color === padrao.background_color &&
    secao.text_color === padrao.text_color &&
    secao.layout_width === padrao.layout_width &&
    secao.title_align === padrao.title_align &&
    secao.title_size === padrao.title_size
  );
}

/**
 * A página que vai para salvar_pagina_inicial: na ordem do rascunho (a
 * posição é a ordem), com "acima/abaixo" dos produtos calculado. A grade
 * inventada só vai se a pessoa mexeu nela (lugar ou aparência); parada no
 * fim e igual, a loja continua inventando, como antes.
 */
/** As seções que viram linha no banco, na ordem (a grade inventada só se mexeram nela). */
export function secoesQueVaoAoBanco(secoes: SecaoDoEditor[]): SecaoDoEditor[] {
  const ligadas = secoes.filter((s) => s.ativa);
  const virtual = secoes.find((s) => s.virtual);
  const levarVirtual = Boolean(virtual && (ligadas[ligadas.length - 1]?.chave !== virtual.chave || !mesmaGradeInventada(virtual)));
  return secoes.filter((s) => !s.virtual || levarVirtual);
}

export function paginaParaGravar(secoes: SecaoDoEditor[]): SecaoParaGravar[] {
  const lista = secoesQueVaoAoBanco(secoes);
  const grade = lista.find((s) => s.modo === "all_products" && s.ativa);
  const ordemDasLigadas = lista.filter((s) => s.ativa).map((s) => s.chave);
  const lugarDaGrade = grade ? ordemDasLigadas.indexOf(grade.chave) : -1;

  return lista.map((secao) => {
    const meu = ordemDasLigadas.indexOf(secao.chave);
    const config: Record<string, unknown> = { ...secao.config, mode: secao.modo };
    const linha: SecaoParaGravar = {
      id: secao.id,
      title: secao.title.trim().slice(0, 200),
      background_color: secao.background_color,
      text_color: secao.text_color,
      placement: lugarDaGrade >= 0 && meu > lugarDaGrade ? "below" : "above",
      layout_width: secao.layout_width,
      title_align: secao.title_align,
      title_size: secao.title_size,
      section_type: secao.modo === "all_products" ? "all_products" : "manual_products",
      config,
      active: secao.ativa,
    };
    if (secao.modo === "manual_products") linha.produtos = secao.produtos.slice(0, LIMITE_DE_PRODUTOS);
    return linha;
  });
}

/** O rascunho mudou em relação ao publicado? */
export function mudou(rascunho: SecaoDoEditor[], publicado: SecaoDoEditor[]) {
  return JSON.stringify(paginaParaGravar(rascunho)) !== JSON.stringify(paginaParaGravar(publicado));
}

/* ------------------------------------------------ conversa com a loja */

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

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

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

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

/** Confere a mensagem que chegou da loja. Qualquer coisa fora do formato vira null. */
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 === "pronto") {
    const arranjo = Array.isArray(m.arranjoAutomatico)
      ? m.arranjoAutomatico.slice(0, LIMITE_DE_SECOES).map(secaoDoArranjo).filter((s): s is SecaoDoEditor => Boolean(s))
      : null;
    return { tipo: "pronto", tema: temaDaLoja(m.tema), homeEditada: m.homeEditada === true, arranjoAutomatico: arranjo };
  }
  if (m.tipo === "acao" && typeof m.acao === "string" && (ACOES as readonly string[]).includes(m.acao)) {
    return {
      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 { tipo: "link-bloqueado" };
  return null;
}

/**
 * O rascunho no formato que a loja desenha (só as seções ligadas).
 * `previaNoAr`: as agendadas que a pessoa quer ver como se já estivessem no
 * ar (só a prévia; nunca vai para o banco).
 */
export function rascunhoParaALoja(secoes: SecaoDoEditor[], previaNoAr?: ReadonlySet<string>) {
  return secoes
    .filter((s) => s.ativa)
    .map((s) => ({
      ...(previaNoAr?.has(s.chave) ? { previa_no_ar: true } : {}),
      id: s.chave,
      rotulo: NOME_DO_MODO[s.modo],
      title: s.title,
      background_color: s.background_color,
      text_color: s.text_color,
      layout_width: s.layout_width,
      title_align: s.title_align,
      title_size: s.title_size,
      section_type: s.modo === "all_products" ? "all_products" : "manual_products",
      config: { ...s.config, mode: s.modo },
      produtos: s.modo === "manual_products" ? s.produtos : [],
    }));
}
