import { canReadFinancialLookup, LEITURAS_DO_FINANCEIRO } from "@/lib/company-context";
import { montarDreAnalitico, type DreAnalyticEntry } from "@/lib/financeiro/dre-analitico";
import { ApiV1Error } from "@/lib/api-v1/errors";
import { listPaymentLinks, resolvePaymentLinksContext } from "@/lib/api-v1/services/payment-links-service";

import type { ContextoDoAssistente } from "../contexto";
import {
  AVISO_DE_DADOS,
  contagem,
  dataBr,
  diasEntre,
  dinheiro,
  exigir,
  falha,
  hojeEmBrasilia,
  limparBusca,
  nomeDoCliente,
  periodoBr,
  recusar,
  somarDias,
  todasAsLinhas,
  um,
} from "./comum";
import {
  intervaloDoMes,
  mesAnterior,
  nomeDaForma,
  nomeDaOrigem,
  nomeDoMes,
  nomeDoTipoDeConta,
  porcento,
  situacaoDaContaAPagar,
  situacaoDaContaAReceber,
} from "./financeiro-completo-apoio";
import {
  VALORES_OCULTOS,
  categoriasFinanceiras,
  contasBancarias,
  empresaDe,
  escolherCategoria,
  escolherConta,
  nomeDaConta,
  podeVerValores,
  saldosDasContas,
} from "./financeiro-completo-comum";
import type { FerramentaDeLeitura } from "./tipos";

/* ----------------------------------------------------- contas a pagar */

type LinhaAPagar = {
  id: string;
  description: string | null;
  amount: number | null;
  due_date: string | null;
  payment_date: string | null;
  status: string | null;
  bank_account_id: string | null;
  category_id: string | null;
  supplier_name: string | null;
  is_recurring: boolean | null;
  payment_method: string | null;
};

const SITUACOES_A_PAGAR = ["em_aberto", "vencidas", "pagas", "canceladas", "todas"] as const;

export const listarContasAPagar: FerramentaDeLeitura = {
  nome: "listar_contas_a_pagar",
  titulo: "Listar contas a pagar",
  descricao:
    "Lista as contas a pagar da empresa (aluguel, fornecedores, impostos…), com valor, vencimento, fornecedor, categoria, conta de onde sai e situação. Use para responder 'o que vence esta semana', 'quanto devo ao fornecedor X', 'quais contas estão vencidas'. Sem filtros, mostra as em aberto (inclusive as vencidas), da mais antiga para a mais nova. O período filtra pelo vencimento (nas pagas, pela data do pagamento). Traz o id de cada conta, que é o que dar_baixa_conta_a_pagar, editar_conta_a_pagar e cancelar_conta_a_pagar pedem.",
  campos: {
    situacao: { tipo: "opcao", descricao: "Quais contas. O padrão é em_aberto (pendentes e vencidas).", opcoes: SITUACOES_A_PAGAR },
    de: { tipo: "data", descricao: "Primeiro dia do período (vencimento; nas pagas, data do pagamento)." },
    ate: { tipo: "data", descricao: "Último dia do período." },
    busca: { tipo: "texto", descricao: "Parte da descrição ou do nome do fornecedor.", max: 80 },
    categoria: { tipo: "texto", descricao: "Nome da categoria (ex.: Aluguel, Insumos).", max: 80 },
    limite: { tipo: "inteiro", descricao: "Quantas contas mostrar. O padrão é 30.", min: 1, max: 100 },
  },
  tipo: "leitura",
  permissoes: ["financial.payables.read"],
  async executar(ctx, args) {
    exigir(ctx, "financial.payables.read", "ver as contas a pagar");
    const hoje = hojeEmBrasilia();
    const situacao = (args.situacao as string | undefined) ?? "em_aberto";
    const limite = Number(args.limite ?? 30);
    const de = args.de as string | undefined;
    const ate = args.ate as string | undefined;
    if (de && ate && ate < de) recusar("O fim do período é antes do início.");
    const busca = limparBusca(args.busca);
    const categoria = await escolherCategoria(ctx, args.categoria);
    const colunaDaData = situacao === "pagas" ? "payment_date" : "due_date";

    const { linhas, cortado } = await todasAsLinhas<LinhaAPagar>((inicio, fim) => {
      let consulta = ctx.admin
        .from("accounts_payable")
        .select("id,description,amount,due_date,payment_date,status,bank_account_id,category_id,supplier_name,is_recurring,payment_method")
        .eq("company_id", empresaDe(ctx));
      if (situacao === "em_aberto") consulta = consulta.in("status", ["PENDING", "OVERDUE"]);
      if (situacao === "vencidas") consulta = consulta.in("status", ["PENDING", "OVERDUE"]).lt("due_date", hoje);
      if (situacao === "pagas") consulta = consulta.eq("status", "PAID");
      if (situacao === "canceladas") consulta = consulta.eq("status", "CANCELED");
      if (de) consulta = consulta.gte(colunaDaData, de);
      if (ate) consulta = consulta.lte(colunaDaData, ate);
      if (busca) consulta = consulta.or(`description.ilike.%${busca}%,supplier_name.ilike.%${busca}%`);
      if (categoria) consulta = consulta.eq("category_id", categoria.id);
      return consulta.order(colunaDaData, { ascending: situacao !== "pagas" }).order("id").range(inicio, fim);
    }, "ler as contas a pagar", 5000);

    const [contas, categorias] = await Promise.all([contasBancarias(ctx), categoriasFinanceiras(ctx)]);
    const nomeDaContaPorId = new Map(contas.map((conta) => [conta.id, nomeDaConta(conta)]));
    const nomeDaCategoria = new Map(categorias.map((item) => [item.id, item.name]));
    const verValores = podeVerValores(ctx);

    const lista = linhas.map((linha) => {
      const situacaoDaLinha = situacaoDaContaAPagar(linha.status, linha.due_date, hoje);
      return {
        id: linha.id,
        descricao: linha.description || "Conta a pagar",
        fornecedor: linha.supplier_name || null,
        valor: verValores ? dinheiro(linha.amount) : null,
        vence_em: linha.due_date,
        situacao: situacaoDaLinha,
        ...(linha.payment_date && linha.status === "PAID" ? { paga_em: linha.payment_date, forma: nomeDaForma(linha.payment_method) } : {}),
        categoria: linha.category_id ? nomeDaCategoria.get(linha.category_id) ?? null : null,
        conta: linha.bank_account_id ? nomeDaContaPorId.get(linha.bank_account_id) ?? null : null,
        ...(linha.is_recurring ? { recorrente: true } : {}),
      };
    });
    const vencidas = lista.filter((linha) => linha.situacao === "vencida");
    const total = dinheiro(linhas.reduce((soma, linha) => soma + Number(linha.amount || 0), 0));

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        filtro: { situacao, ...(de ? { de } : {}), ...(ate ? { ate } : {}), ...(busca ? { busca } : {}), ...(categoria ? { categoria: categoria.name } : {}) },
        encontradas: lista.length,
        total: verValores ? total : null,
        ...(situacao !== "pagas" && situacao !== "canceladas"
          ? { vencidas: { contas: vencidas.length, total: verValores ? dinheiro(linhas.filter((l) => situacaoDaContaAPagar(l.status, l.due_date, hoje) === "vencida").reduce((s, l) => s + Number(l.amount || 0), 0)) : null } }
          : {}),
        contas: lista.slice(0, limite),
        ...(lista.length > limite ? { mostrando: limite } : {}),
        ...(cortado ? { atencao: "Há contas demais para somar de uma vez; o total pode estar abaixo do real. Estreite o período." } : {}),
        ...(verValores ? {} : { valores: VALORES_OCULTOS }),
      },
      resumo: `Consultou as contas a pagar (${situacao.replace("_", " ")}): ${contagem(lista.length, "conta", "contas")}`,
    };
  },
};

/* --------------------------------------------------- contas a receber */

type LinhaAReceber = {
  id: string;
  description: string | null;
  client_name: string | null;
  client_company: string | null;
  total_amount: number | null;
  paid_amount: number | null;
  due_date: string | null;
  status: string | null;
  bank_account_id: string | null;
  category_id: string | null;
};

type PedidoEmAberto = {
  id: string;
  number: number | null;
  total_amount: number | null;
  paid_amount: number | null;
  created_at: string | null;
  delivery_date: string | null;
  financial_status: string | null;
  clients?: { name?: string | null; company_name?: string | null } | Array<{ name?: string | null; company_name?: string | null }> | null;
};

const SITUACOES_A_RECEBER = ["em_aberto", "vencidas", "recebidas", "canceladas", "todas"] as const;

export const listarContasAReceber: FerramentaDeLeitura = {
  nome: "listar_contas_a_receber",
  titulo: "Listar contas a receber",
  descricao:
    "Lista o que a empresa tem a receber: os títulos avulsos (contas a receber lançadas no financeiro) e, junto, os pedidos com saldo em aberto — como a tela Contas a receber mostra. Use para 'quem está me devendo', 'o que vence esta semana para receber', 'quanto falta receber do cliente X'. Sem filtros, mostra o que está em aberto. Cada título traz o id que receber_conta_a_receber pede; cada pedido traz o número que registrar_pagamento_do_pedido aceita.",
  campos: {
    situacao: { tipo: "opcao", descricao: "Quais títulos. O padrão é em_aberto (inclui os recebidos em parte e os vencidos).", opcoes: SITUACOES_A_RECEBER },
    de: { tipo: "data", descricao: "Primeiro dia do vencimento (só para os títulos avulsos)." },
    ate: { tipo: "data", descricao: "Último dia do vencimento (só para os títulos avulsos)." },
    busca: { tipo: "texto", descricao: "Parte da descrição ou do nome do cliente.", max: 80 },
    incluir_pedidos: { tipo: "booleano", descricao: "Trazer também os pedidos com saldo em aberto. O padrão é sim quando a situação é em_aberto ou todas." },
    limite: { tipo: "inteiro", descricao: "Quantos itens mostrar de cada lista. O padrão é 30.", min: 1, max: 100 },
  },
  tipo: "leitura",
  permissoes: ["financial.receivables.read"],
  async executar(ctx, args) {
    exigir(ctx, "financial.receivables.read", "ver as contas a receber");
    const hoje = hojeEmBrasilia();
    const situacao = (args.situacao as string | undefined) ?? "em_aberto";
    const limite = Number(args.limite ?? 30);
    const de = args.de as string | undefined;
    const ate = args.ate as string | undefined;
    if (de && ate && ate < de) recusar("O fim do período é antes do início.");
    const busca = limparBusca(args.busca);
    const verValores = podeVerValores(ctx);
    const abertos = ["PENDING", "PARTIALLY_PAID", "OVERDUE"];

    const { linhas, cortado } = await todasAsLinhas<LinhaAReceber>((inicio, fim) => {
      let consulta = ctx.admin
        .from("accounts_receivable")
        .select("id,description,client_name,client_company,total_amount,paid_amount,due_date,status,bank_account_id,category_id")
        .eq("company_id", empresaDe(ctx));
      if (situacao === "em_aberto") consulta = consulta.in("status", abertos);
      if (situacao === "vencidas") consulta = consulta.in("status", abertos).lt("due_date", hoje);
      if (situacao === "recebidas") consulta = consulta.eq("status", "PAID");
      if (situacao === "canceladas") consulta = consulta.eq("status", "CANCELED");
      if (de) consulta = consulta.gte("due_date", de);
      if (ate) consulta = consulta.lte("due_date", ate);
      if (busca) consulta = consulta.or(`description.ilike.%${busca}%,client_name.ilike.%${busca}%,client_company.ilike.%${busca}%`);
      return consulta.order("due_date", { ascending: true }).order("id").range(inicio, fim);
    }, "ler as contas a receber", 5000);

    const titulos = linhas.map((linha) => {
      const total = dinheiro(linha.total_amount);
      const recebido = dinheiro(linha.paid_amount);
      return {
        id: linha.id,
        descricao: linha.description || "Conta a receber",
        cliente: linha.client_company || linha.client_name || null,
        valor: verValores ? total : null,
        recebido: verValores ? recebido : null,
        falta_receber: verValores ? dinheiro(Math.max(0, total - recebido)) : null,
        vence_em: linha.due_date,
        situacao: situacaoDaContaAReceber(linha.status, linha.due_date, hoje),
      };
    });
    const faltaNosTitulos = dinheiro(
      linhas.filter((linha) => linha.status !== "CANCELED").reduce((soma, linha) => soma + Math.max(0, Number(linha.total_amount || 0) - Number(linha.paid_amount || 0)), 0)
    );

    const querPedidos = args.incluir_pedidos !== undefined ? args.incluir_pedidos === true : situacao === "em_aberto" || situacao === "todas";
    let pedidos: Array<Record<string, unknown>> = [];
    let faltaNosPedidos = 0;
    let pedidosCortados = false;
    if (querPedidos) {
      const lidos = await todasAsLinhas<PedidoEmAberto>(
        (inicio, fim) =>
          ctx.supabase
            .from("orders")
            .select("id,number,total_amount,paid_amount,created_at,delivery_date,financial_status,clients(name,company_name)")
            .in("user_id", ctx.tenantIds)
            .neq("status", "canceled")
            .neq("financial_status", "paid")
            .order("created_at", { ascending: true })
            .range(inicio, fim),
        "ler os pedidos em aberto",
        5000
      );
      pedidosCortados = lidos.cortado;
      const termo = busca.toLowerCase().replace(/^#/, "");
      const emAberto = lidos.linhas
        .map((pedido) => {
          const total = dinheiro(pedido.total_amount);
          const pago = dinheiro(pedido.paid_amount);
          return { pedido, falta: dinheiro(Math.max(0, total - pago)), cliente: nomeDoCliente(um(pedido.clients) ?? null) };
        })
        .filter((item) => item.falta > 0.009)
        .filter((item) => !termo || String(item.pedido.number ?? "") === termo || (item.cliente ?? "").toLowerCase().includes(termo));
      faltaNosPedidos = dinheiro(emAberto.reduce((soma, item) => soma + item.falta, 0));
      pedidos = emAberto.map((item) => ({
        pedido_id: item.pedido.id,
        numero: item.pedido.number,
        cliente: item.cliente,
        valor: verValores ? dinheiro(item.pedido.total_amount) : null,
        falta_receber: verValores ? item.falta : null,
        criado_em: String(item.pedido.created_at ?? "").slice(0, 10) || null,
        entrega: item.pedido.delivery_date,
      }));
    }

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        filtro: { situacao, ...(de ? { de } : {}), ...(ate ? { ate } : {}), ...(busca ? { busca } : {}) },
        titulos_avulsos: {
          encontrados: titulos.length,
          falta_receber: verValores ? faltaNosTitulos : null,
          itens: titulos.slice(0, limite),
          ...(titulos.length > limite ? { mostrando: limite } : {}),
        },
        ...(querPedidos
          ? {
              pedidos_com_saldo: {
                encontrados: pedidos.length,
                falta_receber: verValores ? faltaNosPedidos : null,
                itens: pedidos.slice(0, limite),
                ...(pedidos.length > limite ? { mostrando: limite } : {}),
              },
            }
          : {}),
        ...(cortado || pedidosCortados ? { atencao: "Há lançamentos demais para somar de uma vez; os totais podem estar abaixo do real." } : {}),
        ...(verValores ? {} : { valores: VALORES_OCULTOS }),
      },
      resumo: `Consultou as contas a receber (${situacao.replace("_", " ")}): ${contagem(titulos.length, "título", "títulos")}${querPedidos ? ` e ${contagem(pedidos.length, "pedido", "pedidos")} com saldo` : ""}`,
    };
  },
};

/* ---------------------------------------------------- contas bancárias */

export const listarContasBancarias: FerramentaDeLeitura = {
  nome: "listar_contas_bancarias",
  titulo: "Listar contas bancárias e saldos",
  descricao:
    "Lista as contas da empresa (contas bancárias, contas digitais e caixas físicos) com o saldo atual de cada uma e o total. Use para 'quanto tenho em caixa', 'qual o saldo do banco', ou para achar o nome certo da conta antes de dar baixa, receber ou transferir.",
  campos: {
    incluir_bloqueadas: { tipo: "booleano", descricao: "Mostrar também as contas bloqueadas. O padrão é não." },
  },
  tipo: "leitura",
  permissoes: LEITURAS_DO_FINANCEIRO,
  async executar(ctx, args) {
    if (!canReadFinancialLookup(ctx.empresa, ctx.user.id)) {
      exigir(ctx, "financial.bank.read", "ver as contas bancárias");
    }
    const todas = await contasBancarias(ctx);
    const contas = args.incluir_bloqueadas === true ? todas : todas.filter((conta) => conta.active !== false);
    const verValores = podeVerValores(ctx);
    const saldos = verValores ? await saldosDasContas(ctx, todas) : new Map<string, number>();
    const lista = contas.map((conta) => ({
      id: conta.id,
      nome: nomeDaConta(conta),
      tipo: nomeDoTipoDeConta(conta.type),
      banco: conta.bank_name || null,
      saldo_atual: verValores ? saldos.get(conta.id) ?? 0 : null,
      ...(conta.active === false ? { bloqueada: true } : {}),
    }));
    const total = dinheiro(lista.filter((conta) => !conta.bloqueada).reduce((soma, conta) => soma + Number(conta.saldo_atual || 0), 0));
    const caixas = dinheiro(
      lista.filter((conta, i) => !conta.bloqueada && String(contas[i].type).toUpperCase() === "CAIXA").reduce((soma, conta) => soma + Number(conta.saldo_atual || 0), 0)
    );
    return {
      dados: {
        contas: lista,
        saldo_total: verValores ? total : null,
        ...(verValores ? { em_caixa_fisico: caixas, em_bancos: dinheiro(total - caixas) } : { valores: VALORES_OCULTOS }),
      },
      resumo: `Consultou as contas bancárias (${contagem(lista.length, "conta", "contas")})`,
    };
  },
};

export const listarCategoriasFinanceiras: FerramentaDeLeitura = {
  nome: "listar_categorias_financeiras",
  titulo: "Listar categorias do financeiro",
  descricao:
    "Lista as categorias que a empresa usa para classificar contas a pagar e a receber (Aluguel, Insumos, Impostos…). Use para escolher a categoria certa ao criar ou editar uma conta. Não confundir com as categorias de produtos.",
  campos: {},
  tipo: "leitura",
  permissoes: LEITURAS_DO_FINANCEIRO,
  async executar(ctx) {
    if (!canReadFinancialLookup(ctx.empresa, ctx.user.id)) {
      exigir(ctx, "financial.payables.read", "ver as categorias do financeiro");
    }
    const categorias = await categoriasFinanceiras(ctx);
    return {
      dados: {
        categorias: categorias.map((categoria) => ({
          id: categoria.id,
          nome: categoria.name,
          tipo: categoria.type === "income" ? "receita" : categoria.type === "expense" ? "despesa" : categoria.type,
        })),
      },
      resumo: `Consultou as categorias do financeiro (${contagem(categorias.length, "categoria", "categorias")})`,
    };
  },
};

/* --------------------------------------------------- fluxo de caixa e DRE */

function lancamento(entrada: DreAnalyticEntry, verValores: boolean) {
  return {
    dia: entrada.date,
    tipo: entrada.type === "IN" ? "entrada" : entrada.type === "OUT" ? "saída" : "outro",
    descricao: entrada.description,
    valor: verValores ? dinheiro(entrada.amount) : null,
    origem: nomeDaOrigem(entrada.origin),
    conta: entrada.bank_account_name || null,
    ...(entrada.payment_method ? { forma: nomeDaForma(entrada.payment_method) } : {}),
    ...(entrada.category_name ? { categoria: entrada.category_name } : {}),
  };
}

async function dreDoIntervalo(ctx: ContextoDoAssistente, inicio: string, depois: string, contaId: string) {
  const resultado = await montarDreAnalitico({
    supabase: ctx.supabase,
    leitura: ctx.admin,
    companyId: empresaDe(ctx),
    tenantIds: ctx.tenantIds,
    intervalo: { start: inicio, end: depois },
    bankAccountId: contaId,
    origin: "",
    type: "",
  });
  if (!resultado.ok) falha("ler o fluxo de caixa", { message: String(resultado.corpo.message ?? resultado.corpo.error ?? "") });
  return resultado.corpo;
}

export const fluxoDeCaixa: FerramentaDeLeitura = {
  nome: "fluxo_de_caixa",
  titulo: "Fluxo de caixa",
  descricao:
    "Mostra o dinheiro que de fato entrou e saiu num período: total de entradas, de saídas e o saldo do período, separado por origem (pedidos, contas a receber, contas a pagar, ajustes), entradas por forma de pagamento e por conta, saídas por categoria, os maiores lançamentos e o saldo atual de cada conta. Pode filtrar por uma conta e listar os lançamentos. Sem datas, mostra do dia 1 do mês até hoje. Para o que ainda vai vencer, use listar_contas_a_pagar e listar_contas_a_receber.",
  campos: {
    de: { tipo: "data", descricao: "Primeiro dia. O padrão é o dia 1 do mês atual." },
    ate: { tipo: "data", descricao: "Último dia. O padrão é hoje." },
    conta: { tipo: "texto", descricao: "Nome ou id de uma conta, para ver só ela.", max: 120 },
    listar_lancamentos: { tipo: "booleano", descricao: "Trazer a lista dos lançamentos do período (até 150). O padrão é não." },
  },
  tipo: "leitura",
  permissoes: ["financial.cashflow.read"],
  async executar(ctx, args) {
    exigir(ctx, "financial.cashflow.read", "ver o fluxo de caixa");
    const hoje = hojeEmBrasilia();
    const de = (args.de as string | undefined) ?? `${hoje.slice(0, 8)}01`;
    const ate = (args.ate as string | undefined) ?? hoje;
    if (ate < de) recusar("O fim do período é antes do início.");
    if (diasEntre(de, ate) > 92) recusar("Período grande demais. Peça no máximo 3 meses de cada vez (para o mês inteiro, use dre_do_mes).");
    const conta = args.conta ? await escolherConta(ctx, { dito: args.conta, papel: "a conta" }) : null;

    const dre = await dreDoIntervalo(ctx, de, somarDias(ate, 1), conta?.id ?? "");
    const verValores = podeVerValores(ctx);
    const v = (valor: number) => (verValores ? dinheiro(valor) : null);
    const entradas = dre.entries;

    const porOrigem = new Map<string, { entradas: number; saidas: number }>();
    const porConta = new Map<string, { entradas: number; saidas: number }>();
    for (const entrada of entradas) {
      const ehEntrada = entrada.type === "IN";
      const ehSaida = entrada.type === "OUT";
      if (!ehEntrada && !ehSaida) continue;
      const origem = porOrigem.get(entrada.origin) ?? { entradas: 0, saidas: 0 };
      const daConta = porConta.get(entrada.bank_account_name || "Sem conta") ?? { entradas: 0, saidas: 0 };
      if (ehEntrada) {
        origem.entradas += entrada.amount;
        daConta.entradas += entrada.amount;
      } else {
        origem.saidas += entrada.amount;
        daConta.saidas += entrada.amount;
      }
      porOrigem.set(entrada.origin, origem);
      porConta.set(entrada.bank_account_name || "Sem conta", daConta);
    }

    const todas = await contasBancarias(ctx);
    const saldos = verValores ? await saldosDasContas(ctx, todas) : new Map<string, number>();

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        periodo: { de, ate },
        ...(conta ? { conta: nomeDaConta(conta) } : {}),
        entradas: v(dre.kpis.revenue),
        saidas: v(dre.kpis.expenses),
        saldo_do_periodo: v(dre.kpis.net),
        por_origem: [...porOrigem.entries()].map(([origem, soma]) => ({ origem: nomeDaOrigem(origem), entradas: v(soma.entradas), saidas: v(soma.saidas) })),
        por_conta: [...porConta.entries()].map(([nome, soma]) => ({ conta: nome, entradas: v(soma.entradas), saidas: v(soma.saidas), saldo: v(soma.entradas - soma.saidas) })),
        entradas_por_forma: dre.breakdowns.revenueByMethod.map((item) => ({ forma: nomeDaForma(item.method), total: v(item.total), parte: verValores ? `${porcento(item.total, dre.kpis.revenue)}%` : null })),
        saidas_por_categoria: dre.breakdowns.expenseByCategory.map((item) => ({ categoria: item.category_name, total: v(item.total), parte: verValores ? `${porcento(item.total, dre.kpis.expenses)}%` : null })),
        maiores_entradas: dre.breakdowns.topIn.slice(0, 5).map((item) => lancamento(item, verValores)),
        maiores_saidas: dre.breakdowns.topOut.slice(0, 5).map((item) => lancamento(item, verValores)),
        saldo_atual_das_contas: todas
          .filter((item) => item.active !== false && (!conta || item.id === conta.id))
          .map((item) => ({ conta: nomeDaConta(item), tipo: nomeDoTipoDeConta(item.type), saldo: verValores ? saldos.get(item.id) ?? 0 : null })),
        ...(args.listar_lancamentos === true
          ? { lancamentos: entradas.slice(0, 150).map((item) => lancamento(item, verValores)), ...(entradas.length > 150 ? { lancamentos_mostrados: 150 } : {}) }
          : {}),
        observacao_sobre_transferencias: "Transferência entre contas da empresa aparece como uma saída numa conta e uma entrada na outra; no total do período elas se anulam.",
        ...(entradas.length >= 1000 ? { atencao: "O período tem lançamentos demais para uma leitura só; os totais podem estar abaixo do real. Peça um período menor." } : {}),
        ...(verValores ? {} : { valores: VALORES_OCULTOS }),
      },
      resumo: `Consultou o fluxo de caixa ${periodoBr(de, ate)}${conta ? ` da conta ${nomeDaConta(conta)}` : ""}`,
    };
  },
};

export const dreDoMes: FerramentaDeLeitura = {
  nome: "dre_do_mes",
  titulo: "Resultado do mês (DRE)",
  descricao:
    "Mostra o resultado de um mês, como a tela DRE do financeiro: receita (o que entrou), despesas (o que saiu), resultado e margem, comparado com o mês anterior, com a receita por forma de pagamento e por conta e as despesas por categoria. Use para 'a empresa deu lucro em setembro?', 'quanto gastei com insumos este mês'. Sem mês, mostra o mês atual (até hoje).",
  campos: {
    mes: { tipo: "texto", descricao: "O mês no formato AAAA-MM (ex.: 2026-09). O padrão é o mês atual.", max: 7 },
  },
  tipo: "leitura",
  permissoes: ["financial.dre.read"],
  async executar(ctx, args) {
    exigir(ctx, "financial.dre.read", "ver o resultado do mês");
    const mes = (args.mes as string | undefined)?.trim() || hojeEmBrasilia().slice(0, 7);
    const intervalo = intervaloDoMes(mes);
    if (!intervalo) recusar('Mês inválido. Use o formato AAAA-MM, por exemplo "2026-09".');
    const anterior = mesAnterior(mes);
    const intervaloAnterior = intervaloDoMes(anterior)!;

    const [atual, passado] = await Promise.all([
      dreDoIntervalo(ctx, intervalo.inicio, intervalo.depois, ""),
      dreDoIntervalo(ctx, intervaloAnterior.inicio, intervaloAnterior.depois, ""),
    ]);
    const verValores = podeVerValores(ctx);
    const v = (valor: number) => (verValores ? dinheiro(valor) : null);
    /* Porcentagem, margem e lucro/prejuízo também contam o dinheiro: quem não
       vê valores não vê nenhum deles. */
    const pct = (valor: number) => (verValores ? `${Math.round(valor * 10) / 10}%` : null);
    const variacao = (agora: number, antes: number) =>
      verValores && antes !== 0 ? `${Math.round(((agora - antes) / Math.abs(antes)) * 1000) / 10}%` : null;

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        mes: nomeDoMes(mes),
        periodo: { de: intervalo.inicio, ate: intervalo.fim },
        receita: v(atual.kpis.revenue),
        despesas: v(atual.kpis.expenses),
        resultado: v(atual.kpis.net),
        margem: pct(atual.kpis.margin),
        situacao: !verValores ? null : atual.kpis.net > 0 ? "lucro" : atual.kpis.net < 0 ? "prejuízo" : "empate",
        mes_anterior: {
          mes: nomeDoMes(anterior),
          receita: v(passado.kpis.revenue),
          despesas: v(passado.kpis.expenses),
          resultado: v(passado.kpis.net),
          variacao_da_receita: variacao(atual.kpis.revenue, passado.kpis.revenue),
          variacao_das_despesas: variacao(atual.kpis.expenses, passado.kpis.expenses),
        },
        receita_por_forma: atual.breakdowns.revenueByMethod.map((item) => ({ forma: nomeDaForma(item.method), total: v(item.total), parte: pct(item.pct) })),
        receita_por_conta: atual.breakdowns.revenueByBank.map((item) => ({ conta: item.bank_name, total: v(item.total), parte: pct(item.pct) })),
        despesas_por_categoria: atual.breakdowns.expenseByCategory.map((item) => ({ categoria: item.category_name, total: v(item.total), parte: pct(item.pct) })),
        maiores_entradas: atual.breakdowns.topIn.slice(0, 5).map((item) => lancamento(item, verValores)),
        maiores_saidas: atual.breakdowns.topOut.slice(0, 5).map((item) => lancamento(item, verValores)),
        como_e_calculado:
          "É o regime de caixa: conta o dinheiro que entrou e saiu das contas no mês (pagamentos de pedidos, recebimentos, contas pagas e ajustes), não o que foi vendido ou comprado a prazo. Pedidos cancelados não entram.",
        ...(atual.entries.length >= 1000 ? { atencao: "O mês tem lançamentos demais para uma leitura só; os totais podem estar abaixo do real." } : {}),
        ...(verValores ? {} : { valores: VALORES_OCULTOS }),
      },
      resumo: `Consultou o resultado de ${nomeDoMes(mes)}`,
    };
  },
};

/* --------------------------------------------------- links de pagamento */

const SITUACAO_DO_LINK: Record<string, string> = { draft: "rascunho", active: "ativo", paused: "pausado", archived: "arquivado" };

/** O contexto que o serviço de links pede, montado a partir do acesso do assistente. */
export async function contextoDosLinks(ctx: ContextoDoAssistente) {
  return resolvePaymentLinksContext({
    request: new Request(`${ctx.origemDoAdmin.replace(/\/$/, "")}/api/mcp`),
    supabase: ctx.supabase,
    user: ctx.user,
    admin: ctx.admin as Parameters<typeof resolvePaymentLinksContext>[0]["admin"],
  });
}

export function erroDoServicoDeLinks(erro: unknown, oQue: string): never {
  if (erro instanceof ApiV1Error && erro.status < 500) recusar(erro.message);
  return falha(oQue, erro as { message?: string });
}

export const listarLinksDePagamento: FerramentaDeLeitura = {
  nome: "listar_links_de_pagamento",
  titulo: "Listar links de pagamento",
  descricao:
    "Lista os links de pagamento da empresa (cobranças avulsas por Pix ou cartão que o cliente paga pelo link), com valor, situação, endereço para enviar ao cliente e quando foi pago pela última vez.",
  campos: {},
  tipo: "leitura",
  permissoes: ["financial.links.read"],
  async executar(ctx) {
    exigir(ctx, "financial.links.read", "ver os links de pagamento");
    let lista;
    try {
      lista = await listPaymentLinks({ resolved: await contextoDosLinks(ctx) });
    } catch (erro) {
      erroDoServicoDeLinks(erro, "ler os links de pagamento");
    }
    const verValores = podeVerValores(ctx);
    const links = lista.links.slice(0, 100).map((link) => ({
      id: link.id,
      titulo: link.title,
      valor: verValores ? dinheiro(link.amount) : null,
      tipo: link.type === "subscription" ? "assinatura" : "cobrança única",
      situacao: SITUACAO_DO_LINK[link.status] ?? link.status,
      link: link.public_url,
      criado_em: dataBr(link.created_at),
      ultimo_pagamento: link.last_paid_at ? dataBr(link.last_paid_at) : null,
      ...(link.expires_at ? { vale_ate: dataBr(link.expires_at) } : {}),
    }));
    return {
      dados: { _observacao: AVISO_DE_DADOS, links, total: lista.links.length, ...(verValores ? {} : { valores: VALORES_OCULTOS }) },
      resumo: `Consultou os links de pagamento (${contagem(lista.links.length, "link", "links")})`,
    };
  },
};

export const LEITURAS_DE_FINANCEIRO = [
  listarContasAPagar,
  listarContasAReceber,
  listarContasBancarias,
  listarCategoriasFinanceiras,
  fluxoDeCaixa,
  dreDoMes,
  listarLinksDePagamento,
];
