import { avisarLoja } from "@/lib/loja/avisar-loja";
import {
  condicoesDoCupom,
  erroDoCupom as erroDaRegra,
  ESTADOS,
  fraseDoCupom,
  lerCupom,
  rotuloDoBeneficio,
  type Cupom as CupomDaRegra,
  type NomesDoCupom,
} from "@/lib/regras-do-cupom";

import type { ContextoDoAssistente } from "../contexto";
import { AVISO_DE_DADOS, dinheiro, exigir, falha, recusar } from "./comum";
import { codigoDoCupom, comecoDoDia, diaEmBrasilia, fimDoDia, situacaoDoCupom } from "./operacao-regras";
import type { FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/*
 * Cupons da loja virtual (tabela catalog_coupons, do dono da empresa). A tela
 * é Loja > Produtos > Cupons, que grava direto do navegador com a permissão
 * "catalog.products.write"; aqui as mesmas regras (lib/regras-do-cupom.ts, a
 * mesma conta que a loja faz no pedido), e a loja é avisada depois.
 *
 * Desde a migration 200 o cupom tem: frete grátis, teto do percentual, onde
 * vale (categorias e produtos, com exclusões), quem pode usar, limites de uso,
 * dias e horário, aplicação automática e "mostrar no carrinho".
 */

type Linha = Record<string, unknown> & { id: string; code: string; active: boolean | null; starts_at: string | null; ends_at: string | null };

const COLUNAS =
  "id,code,name,type,value,max_discount,shipping_max,shipping_states,min_total,min_items,min_scope,scope,scope_category_ids," +
  "scope_product_ids,exclude_category_ids,exclude_product_ids,audience,audience_client_ids,usage_limit,usage_limit_per_client," +
  "weekdays,hours_start,hours_end,application,show_in_store,combinable,active,starts_at,ends_at,created_at";

const DIAS = ["domingo", "segunda", "terca", "quarta", "quinta", "sexta", "sabado"];
const TIPO_PARA_O_BANCO = { porcentagem: "percent", valor: "value", frete_gratis: "free_shipping" } as const;
const TIPO_PARA_O_ASSISTENTE = { percent: "porcentagem", value: "valor", free_shipping: "frete_gratis" } as const;
const ONDE = { loja_toda: "all", categorias: "categories", produtos: "products" } as const;
const QUEM = { todos: "all", logados: "logged", primeira_compra: "first_purchase", clientes: "clients" } as const;

function avisar(ctx: ContextoDoAssistente) {
  avisarLoja(ctx.donoDosDados, new Request(`${ctx.origemDoAdmin}/api/mcp`));
}

async function cuponsDaEmpresa(ctx: ContextoDoAssistente): Promise<Linha[]> {
  const { data, error } = await ctx.supabase
    .from("catalog_coupons")
    .select(COLUNAS)
    .eq("user_id", ctx.donoDosDados)
    .order("created_at", { ascending: false })
    .limit(1000);
  if (error) falha("ler os cupons", error);
  return (data ?? []) as unknown as Linha[];
}

async function usosDaEmpresa(ctx: ContextoDoAssistente) {
  const { data } = await ctx.supabase.rpc("metricas_dos_cupons", { p_loja: ctx.donoDosDados });
  const mapa = new Map<string, { usos: number; pedidos: number; receita: number; desconto: number }>();
  for (const l of (data ?? []) as Array<{ coupon_id: string; usos: number; pedidos: number; receita: number; desconto: number }>) {
    mapa.set(String(l.coupon_id), { usos: Number(l.usos) || 0, pedidos: Number(l.pedidos) || 0, receita: dinheiro(l.receita), desconto: dinheiro(l.desconto) });
  }
  return mapa;
}

/** Os nomes das categorias e produtos que os cupons citam, para a frase. */
async function nomesDosCupons(ctx: ContextoDoAssistente, cupons: CupomDaRegra[]): Promise<NomesDoCupom> {
  const cats = Array.from(new Set(cupons.flatMap((c) => [...c.scope_category_ids, ...c.exclude_category_ids]))).slice(0, 300);
  const prods = Array.from(new Set(cupons.flatMap((c) => [...c.scope_product_ids, ...c.exclude_product_ids]))).slice(0, 300);
  const [a, b] = await Promise.all([
    cats.length ? ctx.supabase.from("categories").select("id,name").in("id", cats) : Promise.resolve({ data: [] }),
    prods.length ? ctx.supabase.from("products").select("id,name").in("id", prods) : Promise.resolve({ data: [] }),
  ]);
  const mapa = (linhas: unknown) => Object.fromEntries(((linhas ?? []) as Array<{ id: string; name: string }>).map((l) => [l.id, l.name]));
  return { categorias: mapa(a.data), produtos: mapa(b.data) };
}

function linhaDoCupom(linha: Linha, nomes: NomesDoCupom, usos?: { usos: number; pedidos: number; receita: number; desconto: number }) {
  const c = lerCupom(linha);
  const agora = Date.now();
  const situacao = c.usage_limit && usos && usos.usos >= c.usage_limit && situacaoDoCupom(linha) === "valendo" ? "esgotado" : situacaoDoCupom(linha);
  return {
    id: c.id,
    codigo: c.code,
    nome: c.name,
    desconto: rotuloDoBeneficio(c),
    tipo: TIPO_PARA_O_ASSISTENTE[c.type],
    valor: c.type === "free_shipping" ? null : dinheiro(c.value),
    como_o_cliente_ve: fraseDoCupom(c, agora, nomes),
    regras: condicoesDoCupom(c, agora, { nomes }),
    aplicacao: c.application === "automatic" ? "automatico" : "codigo",
    mostrar_na_loja: c.show_in_store,
    combina_com_outro: c.combinable,
    comeca: diaEmBrasilia(c.starts_at),
    termina: diaEmBrasilia(c.ends_at),
    situacao,
    usos: usos?.usos ?? 0,
    limite_total: c.usage_limit,
    pedidos_confirmados: usos?.pedidos ?? 0,
    receita: usos?.receita ?? 0,
    desconto_concedido: usos?.desconto ?? 0,
  };
}

const CAMPOS_DO_CUPOM_ALVO = {
  cupom_id: { tipo: "id" as const, descricao: "Id do cupom (de listar_cupons)." },
  codigo: { tipo: "texto" as const, descricao: "Ou o código do cupom. Ex.: BEMVINDO10.", max: 40 },
};

async function acharCupom(ctx: ContextoDoAssistente, args: Record<string, unknown>): Promise<{ cupom: Linha; todos: Linha[] }> {
  if (!args.cupom_id && !args.codigo) recusar("Informe cupom_id ou codigo.");
  const todos = await cuponsDaEmpresa(ctx);
  const codigo = String(args.codigo ?? "").trim().toUpperCase();
  const cupom = args.cupom_id ? todos.find((c) => c.id === args.cupom_id) : todos.find((c) => c.code.toUpperCase() === codigo);
  if (!cupom) recusar("Cupom não encontrado. Use listar_cupons para ver os códigos.");
  return { cupom, todos };
}

/* ------------------------------------------------------------ listar */

export const listarCupons: FerramentaDeLeitura = {
  nome: "listar_cupons",
  titulo: "Ver os cupons da loja",
  descricao:
    "Lista os cupons de desconto da loja virtual: código, desconto, como o cliente vê, regras (onde vale, quem pode usar, limites, dias e horário), se é automático, validade, situação (valendo, desligado, vencido, esgotado, ainda não começou) e os números (usos, pedidos, receita e desconto concedido).",
  campos: {
    situacao: { tipo: "opcao", descricao: "Filtrar: valendo, desligado, vencido, esgotado, futuro ou todos (padrão).", opcoes: ["valendo", "desligado", "vencido", "esgotado", "futuro", "todos"] },
  },
  tipo: "leitura",
  permissoes: ["catalog.products.read"],
  async executar(ctx, args) {
    exigir(ctx, "catalog.products.read", "ver os cupons da loja");
    const filtro = (args.situacao as string | undefined) ?? "todos";
    const alvo = filtro === "futuro" ? "ainda não começou" : filtro;
    const [linhas, usos] = await Promise.all([cuponsDaEmpresa(ctx), usosDaEmpresa(ctx)]);
    const nomes = await nomesDosCupons(ctx, linhas.map((l) => lerCupom(l)));
    const cupons = linhas.map((l) => linhaDoCupom(l, nomes, usos.get(l.id))).filter((c) => alvo === "todos" || c.situacao === alvo);
    return {
      dados: { _observacao: AVISO_DE_DADOS, cupons, total: cupons.length },
      resumo: `Consultou os cupons da loja (${cupons.length})`,
    };
  },
};

/* ------------------------------------------------------------ criar / alterar */

type CargaDoCupom = { id: string | null; dados: Record<string, unknown> };

/** Categorias da loja por id ou pelo nome (sem diferença de maiúscula). */
async function idsDasCategorias(ctx: ContextoDoAssistente, valores: string[], campo: string): Promise<string[]> {
  if (!valores.length) return [];
  const { data, error } = await ctx.supabase.from("categories").select("id,name").eq("user_id", ctx.donoDosDados).limit(2000);
  if (error) falha("ler as categorias", error);
  const lista = (data ?? []) as Array<{ id: string; name: string | null }>;
  return valores.map((v) => {
    const texto = v.trim().toLowerCase();
    const achada = lista.find((c) => c.id === texto) ?? lista.find((c) => String(c.name ?? "").trim().toLowerCase() === texto);
    if (!achada) recusar(`Categoria "${v}" não encontrada (${campo}). Use listar_categorias para ver os nomes.`);
    return achada.id;
  });
}

/** Produtos da empresa por id ou pelo nome exato. */
async function idsDosProdutos(ctx: ContextoDoAssistente, valores: string[], campo: string): Promise<string[]> {
  if (!valores.length) return [];
  const ids = valores.filter((v) => /^[0-9a-f-]{36}$/i.test(v.trim())).map((v) => v.trim().toLowerCase());
  const nomes = valores.filter((v) => !/^[0-9a-f-]{36}$/i.test(v.trim()));
  const achados: Array<{ id: string; name: string | null }> = [];
  if (ids.length) {
    const { data, error } = await ctx.supabase.from("products").select("id,name").in("id", ids).in("user_id", ctx.tenantIds);
    if (error) falha("ler os produtos", error);
    achados.push(...((data ?? []) as Array<{ id: string; name: string | null }>));
  }
  for (const nome of nomes) {
    const { data, error } = await ctx.supabase.from("products").select("id,name").in("user_id", ctx.tenantIds).ilike("name", nome.trim().replace(/[%_\\]/g, "\\$&")).limit(2);
    if (error) falha("ler os produtos", error);
    const lista = (data ?? []) as Array<{ id: string; name: string | null }>;
    if (lista.length !== 1) recusar(`Produto "${nome}" ${lista.length ? "tem mais de um com esse nome" : "não encontrado"} (${campo}). Use buscar_produtos e mande o id.`);
    achados.push(lista[0]);
  }
  for (const id of ids) if (!achados.some((a) => a.id === id)) recusar(`Produto ${id} não é desta empresa (${campo}).`);
  return Array.from(new Set(achados.map((a) => a.id)));
}

async function idsDosClientes(ctx: ContextoDoAssistente, valores: string[]): Promise<string[]> {
  if (!valores.length) return [];
  const ids = Array.from(new Set(valores.map((v) => v.trim().toLowerCase())));
  if (ids.some((id) => !/^[0-9a-f-]{36}$/.test(id))) recusar("Os clientes vão pelo id (use buscar_clientes).");
  const { data, error } = await ctx.supabase.from("clients").select("id").in("id", ids).in("user_id", ctx.tenantIds);
  if (error) falha("ler os clientes", error);
  const achados = new Set(((data ?? []) as Array<{ id: string }>).map((c) => c.id));
  const fora = ids.find((id) => !achados.has(id));
  if (fora) recusar(`Cliente ${fora} não é desta empresa.`);
  return ids;
}

function diasDe(valores: string[]): number[] {
  return Array.from(
    new Set(
      valores.map((v) => {
        const t = v.trim().toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/-feira$/, "");
        const i = DIAS.findIndex((d) => d === t || d.slice(0, 3) === t.slice(0, 3));
        if (i < 0) recusar(`Dia "${v}" não reconhecido. Use: ${DIAS.join(", ")}.`);
        return i;
      })
    )
  ).sort();
}

const APAGAVEIS_DO_CUPOM = [
  "pedido_minimo",
  "itens_minimos",
  "inicio",
  "fim",
  "nome",
  "desconto_maximo",
  "frete_ate",
  "frete_estados",
  "limite_total",
  "limite_por_cliente",
  "dias_da_semana",
  "horario",
  "exclusoes",
];

/** Junta o que já existia com o que muda, no formato do banco. */
async function montarCupom(ctx: ContextoDoAssistente, args: Record<string, unknown>, base: Linha | null, apagar: Set<string>) {
  const antes = base ? lerCupom(base) : null;
  const tipo = (args.tipo as keyof typeof TIPO_PARA_O_BANCO | undefined) ?? (antes ? TIPO_PARA_O_ASSISTENTE[antes.type] : undefined);
  if (!tipo) recusar('Diga o tipo do desconto: "porcentagem", "valor" (em reais) ou "frete_gratis".');
  const lista = (nome: string) => (args[nome] as string[] | undefined) ?? undefined;
  const inicio = args.inicio as string | undefined;
  const fim = args.fim as string | undefined;
  const onde = (args.onde_vale as keyof typeof ONDE | undefined) ? ONDE[args.onde_vale as keyof typeof ONDE] : (antes?.scope ?? "all");
  const quem = (args.quem_pode as keyof typeof QUEM | undefined) ? QUEM[args.quem_pode as keyof typeof QUEM] : (antes?.audience ?? "all");

  const estados = apagar.has("frete_estados") ? [] : lista("frete_estados")?.map((uf) => uf.trim().toUpperCase()) ?? antes?.shipping_states ?? [];
  const foraDoBrasil = estados.find((uf) => !ESTADOS.includes(uf));
  if (foraDoBrasil) recusar(`Estado "${foraDoBrasil}" inválido. Use a sigla (SP, RJ, MG...).`);

  const dados: Record<string, unknown> = {
    type: TIPO_PARA_O_BANCO[tipo],
    value: TIPO_PARA_O_BANCO[tipo] === "free_shipping" ? 0 : args.valor !== undefined ? Number(args.valor) : Number(antes?.value ?? NaN),
    name: apagar.has("nome") ? null : args.nome !== undefined ? String(args.nome).trim() || null : (antes?.name ?? null),
    max_discount: apagar.has("desconto_maximo") ? null : args.desconto_maximo !== undefined ? Number(args.desconto_maximo) : (antes?.max_discount ?? null),
    shipping_max: apagar.has("frete_ate") ? null : args.frete_ate !== undefined ? Number(args.frete_ate) : (antes?.shipping_max ?? null),
    shipping_states: estados,
    min_total: apagar.has("pedido_minimo") ? 0 : args.pedido_minimo !== undefined ? Number(args.pedido_minimo) : Number(antes?.min_total ?? 0),
    min_items: apagar.has("itens_minimos") ? 0 : args.itens_minimos !== undefined ? Number(args.itens_minimos) : Number(antes?.min_items ?? 0),
    min_scope: args.minimo_sobre ? (args.minimo_sobre === "participantes" ? "eligible" : "cart") : (antes?.min_scope ?? "cart"),
    scope: onde,
    scope_category_ids: onde !== "categories" ? [] : lista("categorias") ? await idsDasCategorias(ctx, lista("categorias")!, "categorias") : (antes?.scope_category_ids ?? []),
    scope_product_ids: onde !== "products" ? [] : lista("produtos") ? await idsDosProdutos(ctx, lista("produtos")!, "produtos") : (antes?.scope_product_ids ?? []),
    exclude_category_ids: apagar.has("exclusoes") ? [] : lista("excluir_categorias") ? await idsDasCategorias(ctx, lista("excluir_categorias")!, "excluir_categorias") : (antes?.exclude_category_ids ?? []),
    exclude_product_ids: apagar.has("exclusoes") ? [] : lista("excluir_produtos") ? await idsDosProdutos(ctx, lista("excluir_produtos")!, "excluir_produtos") : (antes?.exclude_product_ids ?? []),
    audience: quem,
    audience_client_ids: quem !== "clients" ? [] : lista("clientes") ? await idsDosClientes(ctx, lista("clientes")!) : (antes?.audience_client_ids ?? []),
    usage_limit: apagar.has("limite_total") ? null : args.limite_total !== undefined ? Number(args.limite_total) : (antes?.usage_limit ?? null),
    usage_limit_per_client: apagar.has("limite_por_cliente") ? null : args.limite_por_cliente !== undefined ? Number(args.limite_por_cliente) : (antes?.usage_limit_per_client ?? null),
    weekdays: apagar.has("dias_da_semana") ? [] : lista("dias_da_semana") ? diasDe(lista("dias_da_semana")!) : (antes?.weekdays ?? []),
    hours_start: apagar.has("horario") ? null : (args.horario_inicio as string | undefined) ?? (antes?.hours_start ?? null),
    hours_end: apagar.has("horario") ? null : (args.horario_fim as string | undefined) ?? (antes?.hours_end ?? null),
    application: args.aplicacao ? (args.aplicacao === "automatico" ? "automatic" : "code") : (antes?.application ?? "code"),
    show_in_store: args.mostrar_na_loja !== undefined ? args.mostrar_na_loja === true : (antes?.show_in_store ?? false),
    combinable: args.combinar !== undefined ? args.combinar === true : (antes?.combinable ?? true),
    starts_at: apagar.has("inicio") ? null : inicio ? comecoDoDia(inicio) : (antes?.starts_at ?? null),
    ends_at: apagar.has("fim") ? null : fim ? fimDoDia(fim) : (antes?.ends_at ?? null),
  };
  return dados;
}

async function previaDoCupom(ctx: ContextoDoAssistente, dados: Record<string, unknown>) {
  const c = lerCupom(dados);
  const nomes = await nomesDosCupons(ctx, [c]);
  return {
    codigo: c.code,
    nome: c.name,
    desconto: rotuloDoBeneficio(c),
    como_o_cliente_ve: fraseDoCupom(c, Date.now(), nomes),
    regras: condicoesDoCupom(c, Date.now(), { nomes }),
    aplicacao: c.application === "automatic" ? "automático (sem código, no carrinho de quem cumprir as regras)" : "com código",
    mostrar_na_loja: c.show_in_store,
    combina_com_outro: c.combinable
      ? c.type === "free_shipping"
        ? "sim: vai junto com um cupom de desconto"
        : "sim: vai junto com um cupom de frete grátis"
      : "não: usado sozinho no pedido",
    comeca: diaEmBrasilia(c.starts_at) ?? "já",
    termina: diaEmBrasilia(c.ends_at) ?? "sem data para acabar",
    ligado: c.active,
  };
}

/*
 * Desconto grande é dinheiro: um cupom que TERMINA ligado com 30% ou mais (ou
 * R$ 100 fixos), ou automático (vale para todo mundo, sem código), passa pela
 * aprovação da pessoa. A conta é sobre o cupom final (o que já existia somado
 * ao que muda), não sobre os argumentos: senão bastava trocar só o tipo de um
 * cupom de 50 reais para ele virar 50%.
 */
function cupomGrande(dados: { type?: unknown; value?: unknown; active?: unknown; application?: unknown }): boolean {
  if (dados.active === false) return false;
  if (dados.application === "automatic") return true;
  const valor = Number(dados.value);
  if (!Number.isFinite(valor)) return false;
  if (dados.type === "percent") return valor >= 30;
  if (dados.type === "value") return valor >= 100;
  return false;
}

function conferir(dados: Record<string, unknown>) {
  const erro = erroDaRegra(lerCupom(dados));
  if (erro) recusar(erro);
}

const CAMPOS_DAS_REGRAS = {
  tipo: { tipo: "opcao" as const, descricao: "porcentagem (ex.: 10%), valor (ex.: R$ 20 de desconto) ou frete_gratis.", opcoes: ["porcentagem", "valor", "frete_gratis"] },
  valor: { tipo: "numero" as const, descricao: "O desconto: a porcentagem (até 100) ou os reais. No frete grátis não vai.", min: 0.01, max: 1_000_000 },
  nome: { tipo: "texto" as const, descricao: 'Nome que o cliente vê ("Desconto aplicado: Semana da caneca"). Opcional.', max: 80 },
  desconto_maximo: { tipo: "numero" as const, descricao: "Teto do desconto em porcentagem, em reais (ex.: 10% até R$ 50).", min: 0.01, max: 1_000_000 },
  frete_ate: { tipo: "numero" as const, descricao: "No frete grátis: abate até este valor do frete (o resto o cliente paga). Sem ele, o frete todo.", min: 0.01, max: 100_000 },
  frete_estados: { tipo: "textos" as const, descricao: "No frete grátis: só para estes estados (siglas: SP, RJ...). Sem ele, todos.", maxItens: 27, max: 2 },
  onde_vale: { tipo: "opcao" as const, descricao: "loja_toda (padrão), categorias ou produtos.", opcoes: ["loja_toda", "categorias", "produtos"] },
  categorias: { tipo: "textos" as const, descricao: "Com onde_vale=categorias: ids ou nomes das categorias (as subcategorias entram junto).", maxItens: 50, max: 120 },
  produtos: { tipo: "textos" as const, descricao: "Com onde_vale=produtos: ids (de buscar_produtos) ou nomes exatos.", maxItens: 100, max: 200 },
  excluir_categorias: { tipo: "textos" as const, descricao: "Categorias onde o cupom nunca vale (ids ou nomes).", maxItens: 50, max: 120 },
  excluir_produtos: { tipo: "textos" as const, descricao: "Produtos onde o cupom nunca vale (ids ou nomes exatos).", maxItens: 100, max: 200 },
  pedido_minimo: { tipo: "numero" as const, descricao: "Só vale a partir deste total em produtos, em reais.", min: 0, max: 10_000_000 },
  itens_minimos: { tipo: "inteiro" as const, descricao: "Só vale a partir desta quantidade de itens no carrinho.", min: 0, max: 100_000 },
  minimo_sobre: { tipo: "opcao" as const, descricao: "O mínimo olha o carrinho todo (padrão) ou só os produtos participantes.", opcoes: ["carrinho", "participantes"] },
  quem_pode: { tipo: "opcao" as const, descricao: "todos (padrão), logados (com conta na loja), primeira_compra ou clientes (escolhidos).", opcoes: ["todos", "logados", "primeira_compra", "clientes"] },
  clientes: { tipo: "textos" as const, descricao: "Com quem_pode=clientes: ids dos clientes (de buscar_clientes).", maxItens: 200, max: 40 },
  limite_total: { tipo: "inteiro" as const, descricao: "Quantas vezes o cupom pode ser usado no total (ex.: os primeiros 100).", min: 1, max: 1_000_000 },
  limite_por_cliente: { tipo: "inteiro" as const, descricao: "Quantas vezes cada cliente pode usar (exige conta na loja).", min: 1, max: 1000 },
  dias_da_semana: { tipo: "textos" as const, descricao: "Só nestes dias: domingo, segunda, terca, quarta, quinta, sexta, sabado.", maxItens: 7, max: 15 },
  horario_inicio: { tipo: "hora" as const, descricao: "Vale a partir desta hora (Brasília), ex.: 18:00." },
  horario_fim: { tipo: "hora" as const, descricao: "Vale até esta hora (Brasília), ex.: 22:00. Pode virar a noite (22:00 às 02:00)." },
  aplicacao: { tipo: "opcao" as const, descricao: "codigo (o cliente digita, padrão) ou automatico (entra sozinho no carrinho de quem cumprir as regras).", opcoes: ["codigo", "automatico"] },
  combinar: {
    tipo: "booleano" as const,
    descricao: "Um pedido leva até dois cupons: um de desconto e um de frete grátis. true (padrão) deixa este ir junto com um do outro tipo; false faz este ser usado sozinho.",
  },
  mostrar_na_loja: { tipo: "booleano" as const, descricao: 'Aparece em "Cupons da loja" no carrinho, para o cliente tocar e usar. O padrão é não (secreto).' },
  inicio: { tipo: "data" as const, descricao: "Primeiro dia em que vale (horário de Brasília)." },
  fim: { tipo: "data" as const, descricao: "Último dia em que vale (até 23:59 de Brasília)." },
};

export const criarCupom: FerramentaDeEscrita<CargaDoCupom> = {
  nome: "criar_cupom",
  titulo: "Criar um cupom de desconto",
  descricao:
    "Cria um cupom para a loja virtual: porcentagem (com teto opcional), valor fixo ou frete grátis; onde vale (loja toda, categorias, produtos, com exclusões); quem pode usar (todos, logados, primeira compra, clientes escolhidos); limites (total e por cliente); dias e horário; com código ou automático; se aparece no carrinho; e se combina com outro cupom (um pedido leva até um de desconto e um de frete grátis; o mínimo do frete grátis olha os produtos antes dos descontos). O código fica em maiúsculas (letras, números e hífen, de 3 a 30) e não pode repetir outro cupom da loja. Nasce ligado, a não ser que mande ativo=false.",
  campos: {
    codigo: { tipo: "texto", descricao: "O código que o cliente digita. Ex.: BEMVINDO10.", obrigatorio: true, max: 40 },
    ...CAMPOS_DAS_REGRAS,
    tipo: { ...CAMPOS_DAS_REGRAS.tipo, obrigatorio: true },
    ativo: { tipo: "booleano", descricao: "Já começa valendo. O padrão é sim." },
  },
  tipo: "escrita",
  permissoes: ["catalog.products.write"],
  async preparar(ctx, args) {
    exigir(ctx, "catalog.products.write", "mexer nos cupons da loja");
    const code = codigoDoCupom(args.codigo);
    if (!code) recusar("Código inválido: use de 3 a 30 letras, números ou hífen, sem espaço. Ex.: BEMVINDO10.");
    const todos = await cuponsDaEmpresa(ctx);
    const repetido = todos.find((c) => c.code.toUpperCase() === code);
    if (repetido) recusar(`Já existe um cupom ${repetido.code} nesta loja (${situacaoDoCupom(repetido)}). Use alterar_cupom para mudar esse.`);
    if (args.tipo !== "frete_gratis" && args.valor === undefined) recusar("Diga o valor do desconto (a porcentagem ou os reais).");

    const dados: Record<string, unknown> = { ...(await montarCupom(ctx, args, null, new Set())), code, active: args.ativo !== false };
    conferir(dados);
    return {
      previa: { o_que_vai_acontecer: "Um cupom novo será criado na loja virtual.", cupom: await previaDoCupom(ctx, dados) },
      resumo: `Prévia do cupom ${code}`,
      avisos: (dados as Record<string, unknown>).application === "automatic" ? ["Automático: entra sozinho no carrinho de todo cliente que cumprir as regras, sem precisar do código."] : [],
      sensivel: cupomGrande(dados),
      carga: { id: null, dados },
    };
  },
  async gravar(ctx, carga) {
    const agora = new Date().toISOString();
    const { data, error } = await ctx.supabase
      .from("catalog_coupons")
      .insert({ ...carga.dados, user_id: ctx.donoDosDados, updated_at: agora })
      .select("id")
      .single();
    if (error?.code === "23505") recusar(`Já existe um cupom ${String(carga.dados.code)} nesta loja.`);
    if (error || !data) falha("criar o cupom", error);
    avisar(ctx);
    const rotulo = rotuloDoBeneficio(lerCupom(carga.dados));
    return {
      dados: { criado: true, cupom_id: (data as { id: string }).id, codigo: carga.dados.code, desconto: rotulo },
      resumo: `Criou o cupom ${String(carga.dados.code)} (${rotulo})`,
    };
  },
};

export const alterarCupom: FerramentaDeEscrita<CargaDoCupom> = {
  nome: "alterar_cupom",
  titulo: "Alterar um cupom",
  descricao:
    "Muda um cupom da loja: código, desconto, onde vale, quem pode usar, limites, dias e horário, aplicação, visibilidade, validade, ou liga/desliga (ativo). Mande só o que muda. Para tirar uma regra, use apagar.",
  campos: {
    ...CAMPOS_DO_CUPOM_ALVO,
    novo_codigo: { tipo: "texto", descricao: "Troca o código do cupom.", max: 40 },
    ...CAMPOS_DAS_REGRAS,
    ativo: { tipo: "booleano", descricao: "true liga o cupom; false desliga (para de valer na loja)." },
    apagar: { tipo: "textos", descricao: `Regras a tirar: ${APAGAVEIS_DO_CUPOM.join(", ")}.`, maxItens: 13, max: 20 },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["catalog.products.write"],
  async preparar(ctx, args) {
    exigir(ctx, "catalog.products.write", "mexer nos cupons da loja");
    const { cupom, todos } = await acharCupom(ctx, args);
    const apagar = new Set(((args.apagar as string[] | undefined) ?? []).map((n) => n.trim().toLowerCase()));
    for (const nome of apagar) {
      if (!APAGAVEIS_DO_CUPOM.includes(nome)) recusar(`Não dá para apagar "${nome}". Use um destes: ${APAGAVEIS_DO_CUPOM.join(", ")}.`);
    }

    let code = cupom.code;
    if (args.novo_codigo) {
      const novo = codigoDoCupom(args.novo_codigo);
      if (!novo) recusar("Código inválido: use de 3 a 30 letras, números ou hífen, sem espaço.");
      const repetido = todos.find((c) => c.id !== cupom.id && c.code.toUpperCase() === novo);
      if (repetido) recusar(`Já existe outro cupom ${repetido.code} nesta loja.`);
      code = novo;
    }

    const dados = {
      ...(await montarCupom(ctx, args, cupom, apagar)),
      code,
      active: args.ativo !== undefined ? args.ativo === true : cupom.active !== false,
    };
    conferir(dados);
    const avisos = cupom.code !== code ? [`Quem tinha o código antigo (${cupom.code}) não vai conseguir usar mais.`] : [];
    return {
      previa: {
        o_que_vai_acontecer: "O cupom será alterado na loja virtual.",
        antes: await previaDoCupom(ctx, cupom),
        depois: await previaDoCupom(ctx, dados),
      },
      resumo: `Prévia da alteração do cupom ${cupom.code}`,
      avisos,
      sensivel: cupomGrande(dados),
      carga: { id: cupom.id, dados },
    };
  },
  async gravar(ctx, carga) {
    const { error } = await ctx.supabase
      .from("catalog_coupons")
      .update({ ...carga.dados, updated_at: new Date().toISOString() })
      .eq("id", carga.id as string)
      .eq("user_id", ctx.donoDosDados);
    if (error?.code === "23505") recusar(`Já existe um cupom ${String(carga.dados.code)} nesta loja.`);
    if (error) falha("alterar o cupom", error);
    avisar(ctx);
    const rotulo = rotuloDoBeneficio(lerCupom(carga.dados));
    return {
      dados: { alterado: true, cupom_id: carga.id, codigo: carga.dados.code, ligado: carga.dados.active, desconto: rotulo },
      resumo: `Alterou o cupom ${String(carga.dados.code)}${carga.dados.active ? "" : " (desligado)"}`,
    };
  },
};

/* ------------------------------------------------------------ excluir */

type CargaDaExclusao = { id: string; codigo: string };

export const excluirCupom: FerramentaDeEscrita<CargaDaExclusao> = {
  nome: "excluir_cupom",
  titulo: "Excluir um cupom",
  descricao:
    "Apaga um cupom da loja. Quem já usou continua com o desconto aplicado; o cupom para de valer para novas compras e os números dele saem da lista. Não tem volta: para só pausar, use alterar_cupom com ativo=false.",
  campos: { ...CAMPOS_DO_CUPOM_ALVO },
  tipo: "escrita",
  sobrescreve: true,
  sensivel: true,
  permissoes: ["catalog.products.write"],
  async preparar(ctx, args) {
    exigir(ctx, "catalog.products.write", "mexer nos cupons da loja");
    const { cupom } = await acharCupom(ctx, args);
    const nomes = await nomesDosCupons(ctx, [lerCupom(cupom)]);
    return {
      previa: { o_que_vai_acontecer: "O cupom será apagado da loja. Não tem volta.", cupom: linhaDoCupom(cupom, nomes) },
      resumo: `Prévia: excluir o cupom ${cupom.code}`,
      avisos: [],
      carga: { id: cupom.id, codigo: cupom.code },
    };
  },
  async gravar(ctx, carga) {
    const { error } = await ctx.supabase.from("catalog_coupons").delete().eq("id", carga.id).eq("user_id", ctx.donoDosDados);
    if (error) falha("excluir o cupom", error);
    avisar(ctx);
    return { dados: { excluido: true, codigo: carga.codigo }, resumo: `Excluiu o cupom ${carga.codigo}` };
  },
};

export const FERRAMENTAS_DE_CUPONS = [listarCupons, criarCupom, alterarCupom, excluirCupom];
