import { resolveCompanyPublicProfileId } from "@/lib/company-context";
import { avisarLojaEEsperar } from "@/lib/loja/avisar-loja";
import { MODELOS_PRONTOS, ehModeloPronto, paginaDoModelo, type DadosParaOModelo } from "@/lib/modelos-da-pagina-inicial";
import { NOME_DO_MODO, NOME_DO_TEMA, ehFamiliaVendor, paginaParaGravar, secaoDaLinha, temaDaLoja, type LinhaDaSecao } from "@/lib/pagina-inicial";
import { slugDePagina } from "@/lib/navegacao-da-loja";

import type { ContextoDoAssistente } from "../contexto";
import { contagem, exigir, falha, recusar } from "./comum";
import type { Ferramenta, FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/*
 * Os recursos de toda a página pelo assistente (migration 199), os mesmos do
 * editor da loja em Catálogo > Produtos > Página inicial:
 *
 * - aplicar_modelo_pronto: troca a página inicial inteira por um modelo
 *   pronto montado com os dados da loja. No editor o modelo vira rascunho;
 *   aqui publica (o assistente não tem rascunho), por isso passa SEMPRE pela
 *   prévia com código. Entra no histórico: dá para voltar.
 * - ver_historico_da_pagina: as 15 últimas publicações (quando, quem, o que
 *   mudou), da página inicial ou de uma página extra.
 * - voltar_versao_da_pagina: publica de novo uma versão do histórico
 *   (voltar_para_publicacao). Também com prévia e código.
 */

const ESCREVER_NA_LOJA = "catalog.products.write" as const;
const LER_A_LOJA = "catalog.products.read" as const;
const SO_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

function perfilDaLoja(ctx: ContextoDoAssistente) {
  return resolveCompanyPublicProfileId(ctx.empresa, ctx.user.id);
}

async function avisar(ctx: ContextoDoAssistente) {
  const ouviu = await avisarLojaEEsperar(perfilDaLoja(ctx), new Request(`${ctx.origemDoAdmin}/api/mcp`));
  return ouviu ? {} : { observacao: "A loja pode levar até dois minutos para mostrar a mudança." };
}

function dataEHora(iso: string) {
  try {
    return new Intl.DateTimeFormat("pt-BR", { timeZone: "America/Sao_Paulo", day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" }).format(new Date(iso));
  } catch {
    return iso;
  }
}

/** A página pelo id ou endereço; vazio = a página inicial. */
async function paginaPedida(ctx: ContextoDoAssistente, pedido: unknown): Promise<{ id: string | null; titulo: string }> {
  const texto = String(pedido ?? "").trim();
  if (!texto || /^(home|inicio|início|pagina inicial|página inicial)$/i.test(texto)) return { id: null, titulo: "Página inicial" };
  const consulta = ctx.supabase.from("catalog_paginas").select("id,titulo").eq("user_id", perfilDaLoja(ctx)).limit(1);
  const { data, error } = await (SO_UUID.test(texto) ? consulta.eq("id", texto.toLowerCase()) : consulta.eq("slug", slugDePagina(texto)));
  if (error) falha("ler as páginas da loja", error);
  const pagina = ((data ?? []) as Array<{ id: string; titulo: string }>)[0];
  if (!pagina) recusar(`Não achei a página "${texto}" nesta loja. Use ver_paginas_da_loja (ou deixe vazio para a página inicial).`);
  return { id: pagina.id, titulo: pagina.titulo };
}

/* ============================================================ modelo pronto */

type CargaDoModelo = { secoes: ReturnType<typeof paginaParaGravar>; tema: string; nome: string };

const aplicarModeloPronto: FerramentaDeEscrita<CargaDoModelo> = {
  nome: "aplicar_modelo_pronto",
  titulo: "Montar a página inicial com um modelo pronto",
  descricao: `Troca a página inicial INTEIRA da loja por um modelo pronto, montado com os dados da própria loja (categorias, produtos, banners, cupom ativo, corte de produção, endereço): ${MODELOS_PRONTOS.map((m) => `${m.chave} (${m.nome}: ${m.resumo})`).join("; ")}. Os textos não inventam preço, frete ou prazo. As seções de agora saem. Publica direto (sempre com prévia e código) e entra no histórico: dá para voltar com voltar_versao_da_pagina. No painel, o mesmo modelo vira só rascunho em Catálogo > Produtos > Página inicial > Modelos prontos.`,
  campos: {
    modelo: { tipo: "opcao", opcoes: MODELOS_PRONTOS.map((m) => m.chave), obrigatorio: true, descricao: "O modelo." },
  },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: [ESCREVER_NA_LOJA],
  async preparar(ctx, args) {
    exigir(ctx, ESCREVER_NA_LOJA, "mexer na página inicial da loja");
    const chave = String(args.modelo ?? "");
    if (!ehModeloPronto(chave)) recusar(`Modelo desconhecido. Use um destes: ${MODELOS_PRONTOS.map((m) => m.chave).join(", ")}.`);
    const dono = perfilDaLoja(ctx);
    const [perfil, categorias, produtos, banners, cupons, combos, secoes] = await Promise.all([
      ctx.supabase
        .from("profiles")
        .select("catalog_theme,catalog_slide_enabled,production_cutoff_enabled,production_cutoff_time,address_street,address_city,address,whatsapp,company_whatsapp")
        .eq("id", dono)
        .maybeSingle(),
      ctx.supabase.from("categories").select("id,name,slug,parent_id").eq("user_id", dono).eq("is_visible", true).order("name", { ascending: true }).limit(1000),
      ctx.supabase
        .from("products")
        .select("id,name,category_id,image_url,show_in_catalog,is_catalog_exclusive,product_kind")
        .in("user_id", ctx.tenantIds)
        .eq("active", true)
        .order("catalog_position", { ascending: true, nullsFirst: false })
        .order("created_at", { ascending: false })
        .limit(2000),
      ctx.supabase.from("catalog_banners").select("image_url,link_type,product_id,link_url,open_in_new_tab").eq("user_id", dono).eq("type", "slide").eq("active", true).order("position", { ascending: true }).limit(8),
      ctx.supabase.from("catalog_coupons").select("id,code,starts_at,ends_at").eq("user_id", dono).eq("active", true).limit(20),
      ctx.supabase.from("product_combos").select("id", { count: "exact", head: true }).eq("user_id", dono).eq("active", true),
      ctx.supabase.from("catalog_sections").select("*").eq("user_id", dono).is("pagina_id", null).order("position", { ascending: true }),
    ]);
    const erro = perfil.error || categorias.error || produtos.error || secoes.error;
    if (erro) falha("ler a loja", erro);
    const p = (perfil.data ?? {}) as Record<string, unknown>;
    const tema = temaDaLoja(p.catalog_theme);
    const agora = Date.now();
    const cupom = ((cupons.data ?? []) as Array<{ id: string; code: string; starts_at: string | null; ends_at: string | null }>).find(
      (c) => (!c.starts_at || new Date(c.starts_at).getTime() <= agora) && (!c.ends_at || new Date(c.ends_at).getTime() > agora)
    );
    const dados: DadosParaOModelo = {
      tema,
      carrosselDoTopoLigado: p.catalog_slide_enabled === true,
      categorias: (categorias.data ?? []) as DadosParaOModelo["categorias"],
      produtos: ((produtos.data ?? []) as Array<DadosParaOModelo["produtos"][number] & { show_in_catalog?: boolean; is_catalog_exclusive?: boolean; product_kind?: string | null }>).filter(
        (x) => (x.show_in_catalog || x.is_catalog_exclusive) && x.product_kind !== "digital"
      ),
      banners: (banners.data ?? []) as DadosParaOModelo["banners"],
      cupom: cupom ? { id: cupom.id, code: cupom.code } : null,
      corte: { ligado: p.production_cutoff_enabled === true, hora: p.production_cutoff_time ? String(p.production_cutoff_time) : null },
      temEndereco: Boolean(p.address_street || p.address_city || p.address),
      temWhatsapp: Boolean(p.whatsapp || p.company_whatsapp),
      temCombos: (combos.count ?? 0) > 0,
    };
    if (!dados.produtos.length) recusar("A loja ainda não tem produtos à mostra: o modelo ficaria vazio. Cadastre ou mostre produtos antes.");
    let lista = paginaDoModelo(chave, dados);
    /* A grade "Todos os produtos" de hoje continua a mesma linha (não nasce outra). */
    const linhas = (secoes.data ?? []) as LinhaDaSecao[];
    const gradeDeHoje = linhas.map((linha) => secaoDaLinha(linha, [])).find((s) => s.modo === "all_products");
    if (gradeDeHoje && !ehFamiliaVendor(tema)) lista = lista.map((s) => (s.modo === "all_products" ? { ...gradeDeHoje, ativa: true } : s));
    const nome = MODELOS_PRONTOS.find((m) => m.chave === chave)!.nome;
    const saem = linhas.filter((l) => !lista.some((s) => s.id === l.id)).length;
    return {
      previa: {
        o_que_vai_acontecer: `A página inicial passa a ter ${contagem(lista.length, "seção", "seções")} do modelo "${nome}"${saem ? `; ${contagem(saem, "seção de agora sai", "seções de agora saem")}` : ""}. Fica no histórico: dá para voltar.`,
        secoes: lista.map((s, i) => ({ ordem: i + 1, tipo: NOME_DO_MODO[s.modo], titulo: s.title })),
        ...(ehFamiliaVendor(tema)
          ? { observacao: `No modelo ${NOME_DO_TEMA[tema]}, a página passa a mostrar estas seções (sai a página automática).` }
          : {}),
      },
      resumo: `Prévia: página inicial com o modelo "${nome}"`,
      avisos: [],
      carga: { secoes: paginaParaGravar(lista), tema, nome },
    };
  },
  async gravar(ctx, carga) {
    const { data, error } = await ctx.supabase.rpc("publicar_no_editor_da_loja", {
      p_dono: perfilDaLoja(ctx),
      p_pagina: null,
      p_secoes: carga.secoes,
      p_tema: carga.tema,
      p_navegacao: null,
    });
    if (error) falha("publicar o modelo", error);
    const aviso = await avisar(ctx);
    return {
      dados: {
        publicado: true,
        modelo: carga.nome,
        secoes: carga.secoes.length,
        publicacao: (data as { publicacao?: string } | null)?.publicacao ?? null,
        para_voltar: "ver_historico_da_pagina e voltar_versao_da_pagina (ou Desfazer a última publicação no editor).",
        ...aviso,
      },
      resumo: `Montou a página inicial com o modelo "${carga.nome}"`,
    };
  },
};

/* ================================================================ histórico */

type PublicacaoLida = {
  id: string;
  created_at: string;
  autor: string | null;
  secoes_mexidas: boolean;
  navegacao_mexida: boolean;
  navegacao_antes: Record<string, unknown> | null;
  navegacao_depois: Record<string, unknown> | null;
  restaurada_de: string | null;
};

function oQueMudou(p: PublicacaoLida) {
  const partes: string[] = [];
  if (p.secoes_mexidas) partes.push("seções");
  if (p.navegacao_mexida) {
    if (!p.navegacao_depois) partes.push("menu e rodapé");
    else
      for (const [chave, nome] of [
        ["cabecalho", "cabeçalho"],
        ["menu", "menu"],
        ["rodape", "rodapé"],
      ] as const) {
        if (JSON.stringify(p.navegacao_antes?.[chave] ?? null) !== JSON.stringify(p.navegacao_depois[chave] ?? null)) partes.push(nome);
      }
  }
  return partes;
}

const verHistoricoDaPagina: FerramentaDeLeitura = {
  nome: "ver_historico_da_pagina",
  titulo: "Ver o histórico de publicações de uma página",
  descricao:
    "As 15 últimas publicações da página inicial (ou de uma página extra): quando, quem publicou e o que mudou (seções, cabeçalho, menu, rodapé). O id de cada uma serve para voltar_versao_da_pagina. A primeira da lista é a que está na loja agora.",
  campos: {
    pagina: { tipo: "texto", max: 80, descricao: "A página extra (id ou endereço). Vazio = a página inicial." },
  },
  tipo: "leitura",
  permissoes: [LER_A_LOJA],
  async executar(ctx, args) {
    exigir(ctx, LER_A_LOJA, "ver a loja");
    const pagina = await paginaPedida(ctx, args.pagina);
    const consulta = ctx.supabase
      .from("catalog_home_publicacoes")
      .select("id,created_at,autor,secoes_mexidas,navegacao_mexida,navegacao_antes,navegacao_depois,restaurada_de")
      .eq("user_id", perfilDaLoja(ctx));
    const { data, error } = await (pagina.id ? consulta.eq("pagina_id", pagina.id) : consulta.is("pagina_id", null)).order("created_at", { ascending: false }).limit(15);
    if (error) falha("ler o histórico", error);
    const lista = (data ?? []) as PublicacaoLida[];
    return {
      dados: {
        pagina: pagina.titulo,
        publicacoes: lista.map((p, i) => ({
          id: p.id,
          quando: dataEHora(p.created_at),
          por: p.autor || "alguém da equipe",
          mudou: oQueMudou(p),
          ...(i === 0 ? { na_loja_agora: true } : {}),
          ...(p.restaurada_de ? { voltou_a_uma_versao_anterior: true } : {}),
        })),
        ...(lista.length ? {} : { observacao: "Esta página ainda não foi publicada pelo editor nem pelo assistente." }),
      },
      resumo: `Consultou o histórico de publicações (${pagina.titulo}, ${contagem(lista.length, "publicação", "publicações")})`,
    };
  },
};

type CargaDaVolta = { id: string; quando: string; pagina: string };

const voltarVersaoDaPagina: FerramentaDeEscrita<CargaDaVolta> = {
  nome: "voltar_versao_da_pagina",
  titulo: "Voltar uma página para uma versão do histórico",
  descricao:
    "Publica de novo uma versão do histórico (de ver_historico_da_pagina): as seções da página voltam a ser como ficaram naquela publicação, e o cabeçalho, o menu e o rodapé também, se ela mexeu neles. Vira uma publicação nova (dá para desfazer). Sempre com prévia e código.",
  campos: {
    publicacao_id: { tipo: "id", obrigatorio: true, descricao: "O id da publicação (de ver_historico_da_pagina)." },
  },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: [ESCREVER_NA_LOJA],
  async preparar(ctx, args) {
    exigir(ctx, ESCREVER_NA_LOJA, "mexer na página inicial da loja");
    const id = String(args.publicacao_id ?? "").toLowerCase();
    if (!SO_UUID.test(id)) recusar("Use o id de uma publicação de ver_historico_da_pagina.");
    const { data, error } = await ctx.supabase.rpc("versao_da_publicacao", { p_dono: perfilDaLoja(ctx), p_publicacao: id });
    if (error) recusar(error.message || "Essa publicação não está mais no histórico.");
    const versao = (data ?? {}) as { secoes?: LinhaDaSecao[]; publicada_em?: string; pagina_id?: string | null; navegacao_mexida?: boolean };
    let titulo = "Página inicial";
    if (versao.pagina_id) {
      const { data: pagina } = await ctx.supabase.from("catalog_paginas").select("titulo").eq("id", versao.pagina_id).maybeSingle();
      titulo = String((pagina as { titulo?: string } | null)?.titulo || "Página extra");
    }
    const secoes = (versao.secoes ?? []).map((linha) => secaoDaLinha(linha, []));
    const quando = dataEHora(String(versao.publicada_em || ""));
    return {
      previa: {
        o_que_vai_acontecer: `"${titulo}" volta a ser como ficou na publicação de ${quando}${versao.navegacao_mexida ? " (o cabeçalho, o menu e o rodapé dessa publicação também voltam)" : ""}. Vira uma publicação nova, que dá para desfazer.`,
        secoes_da_versao: secoes.map((s, i) => ({ ordem: i + 1, tipo: NOME_DO_MODO[s.modo], titulo: s.title, ativa: s.ativa })),
      },
      resumo: `Prévia: voltar "${titulo}" para a versão de ${quando}`,
      avisos: [],
      carga: { id, quando, pagina: titulo },
    };
  },
  async gravar(ctx, carga) {
    const { data, error } = await ctx.supabase.rpc("voltar_para_publicacao", { p_dono: perfilDaLoja(ctx), p_publicacao: carga.id });
    if (error) falha("voltar para a versão", error);
    const aviso = await avisar(ctx);
    return {
      dados: { voltou: true, pagina: carga.pagina, versao_de: carga.quando, publicacao_nova: (data as { publicacao?: string } | null)?.publicacao ?? null, ...aviso },
      resumo: `Voltou "${carga.pagina}" para a versão de ${carga.quando}`,
    };
  },
};

export const FERRAMENTAS_DOS_RECURSOS_DA_PAGINA: Ferramenta[] = [aplicarModeloPronto, verHistoricoDaPagina, voltarVersaoDaPagina] as Ferramenta[];
