import { ETAPAS_FIXAS, TIPOS_DE_ACAO, TIPOS_DE_GATILHO } from "@/lib/regras/regras";
import {
  alterarRegra,
  criarRegra,
  ErroDaRegra,
  excluirRegra,
  ligarRegra,
  listarRegras,
  opcoesDasRegras,
  prepararRegra,
  resumoDaRegra,
  testarRegra,
  verExecucoes,
  type AcessoAsRegras,
} from "@/lib/regras/servidor/servico";

import type { ContextoDoAssistente } from "../contexto";
import type { Campos } from "../esquema";
import { exigir, pode, recusar } from "./comum";
import type { Argumentos, Ferramenta, FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

/**
 * Regras da empresa pelo assistente: "quando X, faça Y".
 *
 * Tudo passa por lib/regras/servidor/servico.ts, o mesmo da tela
 * (Configurações > Regras): a conferência, a frase e as travas são as mesmas.
 * Exige company.settings. Criar, alterar e ligar gravam direto; excluir é
 * sensível (prévia com código).
 */

const GATILHOS_EXPLICADOS = [
  "orcamento_sem_resposta (dias): orçamento em aberto (rascunho ou enviado) parado há N dias.",
  "pedido_parado_na_etapa (dias, etapa opcional): pedido há N dias na mesma etapa da produção; sem etapa, vale para aguardando e em produção.",
  "pedido_entrou_na_etapa (etapa): o pedido acabou de entrar na etapa (só vale do momento da criação da regra em diante).",
  "estoque_abaixo (item_tipo insumo|produto, item_id opcional, quantidade): estoque abaixo da quantidade; insumo sem quantidade usa o mínimo do cadastro. Enquanto continuar baixo, lembra de novo a cada 7 dias.",
  "cliente_sem_comprar (dias): cliente cuja última compra foi há N dias.",
  "conta_a_pagar_vencendo (dias): faltam N dias (0 = hoje) para uma conta a pagar vencer.",
  "conta_a_receber_vencida (dias): conta a receber vencida há N dias (0 = venceu) e ainda não paga.",
].join(" ");

const ACOES_EXPLICADAS = [
  "criar_compromisso (titulo opcional, daqui_a_dias 0-30, lembrete_minutos opcional, pessoa_id opcional = responsável): marca um compromisso na agenda ligado ao cliente/pedido/orçamento.",
  "avisar_pessoa (pessoa_id opcional = dono, mensagem opcional): põe o aviso na agenda de hoje da pessoa e manda ao celular dela se as notificações estiverem ligadas.",
  "adicionar_etiqueta_ao_cliente (etiqueta): põe a etiqueta no cadastro do cliente (só gatilhos de orçamento, pedido e cliente).",
].join(" ");

const ETAPAS_EXPLICADAS = `Etapa: use o id de listar_regras (opcoes.etapas). As fixas são ${Object.keys(ETAPAS_FIXAS).join(", ")}; as criadas pela empresa começam com "stage-".`;

const CAMPOS_DO_GATILHO: Campos = {
  dias: { tipo: "inteiro", descricao: "Quantos dias (orcamento_sem_resposta, pedido_parado_na_etapa, cliente_sem_comprar, conta_a_pagar_vencendo, conta_a_receber_vencida).", min: 0, max: 730 },
  etapa: { tipo: "texto", descricao: `Etapa do quadro de produção (pedido_parado_na_etapa, pedido_entrou_na_etapa). ${ETAPAS_EXPLICADAS}`, max: 100 },
  item_tipo: { tipo: "opcao", descricao: "Estoque de insumo ou de produto (estoque_abaixo). O padrão é insumo.", opcoes: ["insumo", "produto"] },
  item_id: { tipo: "id", descricao: "Id do insumo ou do produto vigiado (estoque_abaixo; de consultar_estoque ou buscar_produtos). Sem ele, vale para todos com estoque controlado." },
  quantidade: { tipo: "numero", descricao: "Abaixo de quanto dispara (estoque_abaixo), na unidade do item. Ex.: 10 para 10 m².", min: 0, max: 1_000_000 },
};

const CAMPOS_DAS_ACOES: Campos = {
  acoes: {
    tipo: "lista",
    descricao: `O que fazer quando disparar (1 a 3, sem repetir). ${ACOES_EXPLICADAS}`,
    minItens: 1,
    maxItens: 3,
    itens: {
      acao: { tipo: "opcao", descricao: "A ação.", opcoes: TIPOS_DE_ACAO, obrigatorio: true },
      titulo: { tipo: "texto", descricao: "criar_compromisso: começo do título. Ex.: Ligar para o cliente.", max: 80 },
      daqui_a_dias: { tipo: "inteiro", descricao: "criar_compromisso: para quantos dias depois do disparo (0 = no mesmo dia).", min: 0, max: 30 },
      lembrete_minutos: { tipo: "inteiro", descricao: "criar_compromisso: lembrete antes (5, 10, 15, 30, 60, 120, 240, 1440 ou 2880).", min: 5, max: 2880 },
      pessoa_id: { tipo: "id", descricao: "Quem recebe (criar_compromisso: responsável; avisar_pessoa: quem é avisado). Id de listar_regras (opcoes.pessoas). Sem ele, o dono." },
      mensagem: { tipo: "texto", descricao: "avisar_pessoa: o texto do aviso. Ex.: Cobrar o cliente.", max: 200 },
      etiqueta: { tipo: "texto", descricao: "adicionar_etiqueta_ao_cliente: a etiqueta. Ex.: reativar.", max: 40 },
    },
  },
};

function acesso(ctx: ContextoDoAssistente): AcessoAsRegras {
  return {
    supabase: ctx.supabase,
    admin: ctx.admin,
    empresa: { dono: ctx.donoDosDados, tenantIds: ctx.tenantIds },
    usuarioId: ctx.user.id,
    pode: (permissao) => pode(ctx, permissao),
  };
}

/* Os campos soltos do assistente viram o formato da regra. */
function gatilhoDosArgumentos(args: Argumentos) {
  return {
    tipo: args.gatilho,
    parametros: { dias: args.dias, etapa: args.etapa, item_tipo: args.item_tipo, item_id: args.item_id, quantidade: args.quantidade },
  };
}

function acoesDosArgumentos(lista: unknown) {
  return (Array.isArray(lista) ? lista : []).map((item) => {
    const a = item as Record<string, unknown>;
    switch (a.acao) {
      case "criar_compromisso":
        return { tipo: a.acao, parametros: { titulo: a.titulo, daqui_a_dias: a.daqui_a_dias, lembrete_minutos: a.lembrete_minutos, responsavel_id: a.pessoa_id } };
      case "avisar_pessoa":
        return { tipo: a.acao, parametros: { usuario_id: a.pessoa_id, mensagem: a.mensagem } };
      default:
        return { tipo: a.acao, parametros: { etiqueta: a.etiqueta } };
    }
  });
}

/* Erro da regra vira erro que o assistente lê para se corrigir. */
async function comErroLegivel<T>(fazer: () => Promise<T>): Promise<T> {
  try {
    return await fazer();
  } catch (erro) {
    if (erro instanceof ErroDaRegra) recusar(erro.message);
    throw erro;
  }
}

/* ------------------------------------------------------------------ leitura */

export const listarRegrasDaEmpresa: FerramentaDeLeitura = {
  nome: "listar_regras",
  titulo: "Ver as regras automáticas",
  descricao:
    "Lista as regras automáticas da empresa (\"quando X, faça Y\"): o que cada uma faz numa frase, se está ligada, quantas vezes agiu e as últimas vezes. Traz também as opções para criar regras: as etapas do quadro de produção e as pessoas da equipe (ids).",
  campos: {},
  tipo: "leitura",
  permissoes: ["company.settings"],
  async executar(ctx) {
    exigir(ctx, "company.settings", "ver as regras automáticas");
    const [regras, opcoes] = await comErroLegivel(() => Promise.all([listarRegras(acesso(ctx)), opcoesDasRegras(acesso(ctx))]));
    return {
      dados: {
        regras: regras.map((r) => ({
          id: r.id,
          nome: r.nome,
          ligada: r.ativa,
          o_que_faz: r.frase,
          vezes: r.vezes,
          ultima_conferencia: r.ultima_verificacao,
          ...(r.ultimo_erro ? { problema: r.ultimo_erro } : {}),
          ultimas_vezes: r.ultimas_execucoes.map((e) => ({ quando: e.quando, sobre: e.sobre, resultado: e.resultado })),
        })),
        opcoes: { etapas: opcoes.etapas, pessoas: opcoes.pessoas },
        como_funciona: "As regras são conferidas a cada 5 minutos. Cada regra age uma vez só por registro e ocorrência.",
      },
      resumo: `Consultou as regras automáticas (${regras.length})`,
    };
  },
};

export const verExecucoesDasRegras: FerramentaDeLeitura = {
  nome: "ver_execucoes_das_regras",
  titulo: "Ver o que as regras fizeram",
  descricao: "Mostra o histórico das regras automáticas: quando agiram, sobre o quê e se deu certo. Pode filtrar uma regra.",
  campos: {
    regra_id: { tipo: "id", descricao: "Id da regra (de listar_regras). Sem ele, todas." },
    limite: { tipo: "inteiro", descricao: "Quantas linhas, até 200. O padrão é 50.", min: 1, max: 200 },
  },
  tipo: "leitura",
  permissoes: ["company.settings"],
  async executar(ctx, args) {
    exigir(ctx, "company.settings", "ver o histórico das regras");
    const linhas = await comErroLegivel(() => verExecucoes(acesso(ctx), (args.regra_id as string | undefined) ?? null, Number(args.limite ?? 50)));
    return {
      dados: { execucoes: linhas.map((e) => ({ quando: e.quando, regra_id: e.regra_id, sobre: e.sobre, resultado: e.resultado, detalhe: e.detalhe })) },
      resumo: `Consultou o histórico das regras (${linhas.length})`,
    };
  },
};

export const testarRegraDaEmpresa: FerramentaDeLeitura = {
  nome: "testar_regra",
  titulo: "Testar uma regra sem agir",
  descricao:
    "Mostra o que uma regra pegaria agora, sem fazer nada: quantos registros disparam e quais ainda não foram tratados. Use com regra_id de uma regra salva, ou com os mesmos campos de criar_regra para testar antes de criar.",
  campos: {
    regra_id: { tipo: "id", descricao: "Id de uma regra salva (de listar_regras)." },
    gatilho: { tipo: "opcao", descricao: `O gatilho, para testar sem salvar. ${GATILHOS_EXPLICADOS}`, opcoes: TIPOS_DE_GATILHO },
    ...CAMPOS_DO_GATILHO,
    acoes: { ...CAMPOS_DAS_ACOES.acoes, descricao: "As ações (opcional no teste; sem elas, o teste usa avisar_pessoa)." } as Campos[string],
  },
  tipo: "leitura",
  permissoes: ["company.settings"],
  async executar(ctx, args) {
    exigir(ctx, "company.settings", "testar regras");
    if (!args.regra_id && !args.gatilho) recusar("Mande regra_id de uma regra salva, ou o gatilho com os parâmetros para testar antes de criar.");
    const teste = await comErroLegivel(() =>
      args.regra_id
        ? testarRegra(acesso(ctx), { id: String(args.regra_id) })
        : testarRegra(acesso(ctx), {
            regra: {
              gatilho: gatilhoDosArgumentos(args),
              acoes: args.acoes ? acoesDosArgumentos(args.acoes) : [{ tipo: "avisar_pessoa", parametros: {} }],
            },
          })
    );
    return {
      dados: { nada_foi_feito: true, ...teste },
      resumo: `Testou a regra "${teste.nome}" (${teste.total_agora} registro(s) agora)`,
    };
  },
};

/* ------------------------------------------------------------------- escrita */

type CargaDaRegra = { entrada: Record<string, unknown> };

export const criarRegraDaEmpresa: FerramentaDeEscrita<CargaDaRegra> = {
  nome: "criar_regra",
  titulo: "Criar uma regra automática",
  descricao: `Cria uma regra automática "quando X, faça Y", conferida a cada 5 minutos. Ex.: orçamento 3 dias sem resposta → compromisso para ligar; pedido 2 dias parado na mesma etapa → avisar o dono; estoque de lona abaixo de 10 → avisar; cliente 90 dias sem comprar → etiqueta "reativar"; conta a pagar vencendo em 2 dias → avisar. Gatilhos: ${GATILHOS_EXPLICADOS} Ações (nada vai ao cliente, nada mexe em dinheiro): ${ACOES_EXPLICADAS} Antes de criar, testar_regra mostra o que ela pegaria agora.`,
  campos: {
    nome: { tipo: "texto", descricao: "Nome curto da regra. Sem ele, o nome é a frase do gatilho.", max: 120 },
    gatilho: { tipo: "opcao", descricao: "Quando a regra dispara.", opcoes: TIPOS_DE_GATILHO, obrigatorio: true },
    ...CAMPOS_DO_GATILHO,
    ...CAMPOS_DAS_ACOES,
    ligada: { tipo: "booleano", descricao: "Já nasce ligada? O padrão é sim." },
  },
  tipo: "escrita",
  permissoes: ["company.settings"],
  async preparar(ctx, args) {
    exigir(ctx, "company.settings", "criar regras automáticas");
    const entrada = { nome: args.nome, gatilho: gatilhoDosArgumentos(args), acoes: acoesDosArgumentos(args.acoes) };
    const { regra, frase } = await comErroLegivel(() => prepararRegra(acesso(ctx), entrada));
    return {
      previa: { o_que_vai_acontecer: "Uma regra automática nova será criada.", nome: regra.nome, o_que_faz: frase, ligada: args.ligada !== false },
      resumo: `Prévia de regra: ${regra.nome}`,
      avisos: [],
      carga: { entrada: { ...entrada, ativa: args.ligada !== false } },
    };
  },
  async gravar(ctx, carga) {
    const criada = await comErroLegivel(() => criarRegra(acesso(ctx), carga.entrada, carga.entrada.ativa !== false));
    return {
      dados: { criada: true, regra_id: criada.id, nome: criada.nome, o_que_faz: criada.frase, ligada: criada.ativa, proxima_conferencia: "em até 5 minutos" },
      resumo: `Criou a regra "${criada.nome}"`,
    };
  },
};

type CargaDaAlteracao = { id: string; mudancas: { nome?: unknown; gatilho?: unknown; acoes?: unknown; ativa?: unknown } };

export const alterarRegraDaEmpresa: FerramentaDeEscrita<CargaDaAlteracao> = {
  nome: "alterar_regra",
  titulo: "Alterar uma regra automática",
  descricao:
    "Muda uma regra existente: o nome, o gatilho (mande o gatilho com todos os parâmetros dele) ou as ações (mande a lista inteira, ela substitui a anterior). O que não for mandado continua como está. Mesmos gatilhos e ações de criar_regra.",
  campos: {
    regra_id: { tipo: "id", descricao: "Id da regra (de listar_regras).", obrigatorio: true },
    nome: { tipo: "texto", descricao: "Novo nome.", max: 120 },
    gatilho: { tipo: "opcao", descricao: "Novo gatilho (com os parâmetros dele).", opcoes: TIPOS_DE_GATILHO },
    ...CAMPOS_DO_GATILHO,
    acoes: { ...CAMPOS_DAS_ACOES.acoes, minItens: undefined } as Campos[string],
    ligada: { tipo: "booleano", descricao: "Ligar ou desligar junto." },
  },
  tipo: "escrita",
  sobrescreve: true,
  permissoes: ["company.settings"],
  async preparar(ctx, args) {
    exigir(ctx, "company.settings", "alterar regras automáticas");
    const id = String(args.regra_id);
    const temGatilho = args.gatilho !== undefined;
    const soloDoGatilho = ["dias", "etapa", "item_tipo", "item_id", "quantidade"].some((c) => args[c] !== undefined);
    if (soloDoGatilho && !temGatilho) recusar("Para mudar um parâmetro do gatilho, mande o gatilho junto, com todos os parâmetros dele.");
    const mudancas = {
      ...(args.nome !== undefined ? { nome: args.nome } : {}),
      ...(temGatilho ? { gatilho: gatilhoDosArgumentos(args) } : {}),
      ...(args.acoes !== undefined ? { acoes: acoesDosArgumentos(args.acoes) } : {}),
      ...(typeof args.ligada === "boolean" ? { ativa: args.ligada } : {}),
    };
    if (!Object.keys(mudancas).length) recusar("Nada para mudar. Mande o nome, o gatilho, as ações ou ligada.");
    const atual = await comErroLegivel(() => resumoDaRegra(acesso(ctx), id));
    let fraseNova = atual.frase;
    let nomeNovo = atual.nome;
    if (mudancas.nome !== undefined || mudancas.gatilho || mudancas.acoes) {
      const { regra, frase } = await comErroLegivel(() =>
        prepararRegra(acesso(ctx), {
          nome: mudancas.nome ?? atual.nome,
          gatilho: mudancas.gatilho ?? atual.gatilho,
          acoes: mudancas.acoes ?? atual.acoes,
        })
      );
      fraseNova = frase;
      nomeNovo = regra.nome;
    }
    return {
      previa: {
        o_que_vai_acontecer: "A regra será alterada.",
        antes: { nome: atual.nome, o_que_faz: atual.frase, ligada: atual.ativa },
        depois: { nome: nomeNovo, o_que_faz: fraseNova, ligada: typeof mudancas.ativa === "boolean" ? mudancas.ativa : atual.ativa },
      },
      resumo: `Prévia de alteração da regra "${atual.nome}"`,
      avisos: [],
      carga: { id, mudancas },
    };
  },
  async gravar(ctx, carga) {
    const feita = await comErroLegivel(() => alterarRegra(acesso(ctx), carga.id, carga.mudancas));
    return {
      dados: { alterada: true, regra_id: feita.id, nome: feita.nome, ...("frase" in feita ? { o_que_faz: feita.frase } : {}), ligada: feita.ativa },
      resumo: `Alterou a regra "${feita.nome}"`,
    };
  },
};

type CargaDoLiga = { id: string; ativa: boolean; nome: string };

export const ativarOuDesativarRegra: FerramentaDeEscrita<CargaDoLiga> = {
  nome: "ativar_ou_desativar_regra",
  titulo: "Ligar ou desligar uma regra",
  descricao: "Liga ou desliga uma regra automática. Desligada, ela para de agir até ser ligada de novo; o histórico fica.",
  campos: {
    regra_id: { tipo: "id", descricao: "Id da regra (de listar_regras).", obrigatorio: true },
    ligada: { tipo: "booleano", descricao: "true liga, false desliga.", obrigatorio: true },
  },
  tipo: "escrita",
  permissoes: ["company.settings"],
  async preparar(ctx, args) {
    exigir(ctx, "company.settings", "ligar e desligar regras automáticas");
    const atual = await comErroLegivel(() => resumoDaRegra(acesso(ctx), String(args.regra_id)));
    const ativa = args.ligada === true;
    return {
      previa: { o_que_vai_acontecer: ativa ? "A regra será ligada." : "A regra será desligada.", nome: atual.nome, o_que_faz: atual.frase },
      resumo: `Prévia: ${ativa ? "ligar" : "desligar"} a regra "${atual.nome}"`,
      avisos: atual.ativa === ativa ? [ativa ? "A regra já está ligada." : "A regra já está desligada."] : [],
      carga: { id: atual.id, ativa, nome: atual.nome },
    };
  },
  async gravar(ctx, carga) {
    await comErroLegivel(() => ligarRegra(acesso(ctx), carga.id, carga.ativa));
    return {
      dados: { regra_id: carga.id, nome: carga.nome, ligada: carga.ativa },
      resumo: `${carga.ativa ? "Ligou" : "Desligou"} a regra "${carga.nome}"`,
    };
  },
};

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

export const excluirRegraDaEmpresa: FerramentaDeEscrita<CargaDaExclusao> = {
  nome: "excluir_regra",
  titulo: "Excluir uma regra automática",
  descricao: "Exclui uma regra automática e o histórico dela. Não desfaz o que ela já fez (compromissos e etiquetas continuam). Para só parar, prefira ativar_ou_desativar_regra.",
  campos: {
    regra_id: { tipo: "id", descricao: "Id da regra (de listar_regras).", obrigatorio: true },
  },
  tipo: "escrita",
  sensivel: true,
  sobrescreve: true,
  permissoes: ["company.settings"],
  async preparar(ctx, args) {
    exigir(ctx, "company.settings", "excluir regras automáticas");
    const atual = await comErroLegivel(() => resumoDaRegra(acesso(ctx), String(args.regra_id)));
    return {
      previa: { o_que_vai_acontecer: "A regra e o histórico dela serão excluídos.", nome: atual.nome, o_que_faz: atual.frase, vezes_que_agiu: atual.vezes },
      resumo: `Prévia de exclusão da regra "${atual.nome}"`,
      avisos: ["O que a regra já fez (compromissos, etiquetas) continua onde está."],
      carga: { id: atual.id, nome: atual.nome },
    };
  },
  async gravar(ctx, carga) {
    await comErroLegivel(() => excluirRegra(acesso(ctx), carga.id));
    return { dados: { excluida: true, regra_id: carga.id, nome: carga.nome }, resumo: `Excluiu a regra "${carga.nome}"` };
  },
};

export const FERRAMENTAS_DE_REGRAS: Ferramenta[] = [
  listarRegrasDaEmpresa,
  verExecucoesDasRegras,
  testarRegraDaEmpresa,
  criarRegraDaEmpresa,
  alterarRegraDaEmpresa,
  ativarOuDesativarRegra,
  excluirRegraDaEmpresa,
] as Ferramenta[];
