import { LEMBRETES_ACEITOS, validar } from "@/lib/agenda/validacao";

import { AVISO_DE_DADOS, contagem, dataBr, diasEntre, dinheiro, exigir, falha, hojeEmBrasilia, nomeDoCliente, periodoBr, recusar, somarDias } from "./comum";
import type { FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

const TIPO: Record<string, string> = {
  pedido_entrega: "entrega de pedido",
  conta_pagar: "conta a pagar",
  conta_receber: "conta a receber",
  link_pagamento: "link de pagamento",
  comissao: "comissão",
  compra_fornecedor: "compra de fornecedor",
  cupom: "cupom",
  compromisso: "compromisso",
};

type LinhaDaAgenda = {
  tipo: string | null;
  origem_id: string | null;
  titulo: string | null;
  subtitulo: string | null;
  data: string | null;
  valor: number | null;
  situacao: string | null;
  categoria_nome: string | null;
};

export function linhaDaAgenda(linha: LinhaDaAgenda) {
  return {
    dia: linha.data,
    o_que: TIPO[linha.tipo ?? ""] ?? linha.tipo,
    titulo: linha.titulo ?? "",
    detalhe: linha.subtitulo || null,
    ...(linha.valor !== null && linha.valor !== undefined ? { valor: dinheiro(linha.valor) } : {}),
    situacao: linha.situacao || null,
    categoria: linha.categoria_nome || null,
    id: linha.origem_id,
  };
}

export const verAgenda: FerramentaDeLeitura = {
  nome: "ver_agenda",
  titulo: "Ver a agenda",
  descricao:
    "Mostra o que está marcado num período: entregas de pedido, contas a pagar e a receber, compras de fornecedor e compromissos. Cada pessoa vê só o que a permissão dela deixa. Sem datas, mostra de hoje até daqui a 7 dias.",
  campos: {
    inicio: { tipo: "data", descricao: "Primeiro dia do período. O padrão é hoje." },
    fim: { tipo: "data", descricao: "Último dia do período. O padrão é 7 dias depois do início." },
  },
  tipo: "leitura",
  permissoes: ["agenda.board.read"],
  async executar(ctx, args) {
    exigir(ctx, "agenda.board.read", "ver a agenda");
    const inicio = (args.inicio as string | undefined) ?? hojeEmBrasilia();
    const fim = (args.fim as string | undefined) ?? somarDias(inicio, 7);
    if (fim < inicio) recusar("O fim do período é antes do início.");
    if (diasEntre(inicio, fim) > 92) recusar("Período grande demais. Peça no máximo 3 meses de cada vez.");

    const { data, error } = await ctx.supabase.rpc("get_agenda_range", { p_inicio: inicio, p_fim: fim });
    if (error) falha("ler a agenda", error);
    const linhas = ((data ?? []) as LinhaDaAgenda[])
      .map(linhaDaAgenda)
      .sort((a, b) => String(a.dia).localeCompare(String(b.dia)));

    return {
      dados: { _observacao: AVISO_DE_DADOS, periodo: { inicio, fim }, itens: linhas.slice(0, 200), total: linhas.length, ...(linhas.length > 200 ? { cortado_em: 200 } : {}) },
      resumo: `Consultou a agenda ${periodoBr(inicio, fim)} (${contagem(linhas.length, "item", "itens")})`,
    };
  },
};

/* -------------------------------------------------------- criar compromisso */

type CargaDoCompromisso = {
  dados: Record<string, unknown>;
  titulo: string;
  dia: string;
};

export const criarCompromisso: FerramentaDeEscrita<CargaDoCompromisso> = {
  nome: "criar_compromisso",
  titulo: "Marcar um compromisso na agenda",
  descricao:
    "Marca um compromisso na agenda da empresa: visita, instalação, retirada, reunião. Pode ser ligado a um cliente e ter lembrete. A primeira chamada devolve uma prévia e um código; depois de a pessoa aprovar, chame de novo com os mesmos dados e o código em `confirmacao`.",
  campos: {
    titulo: { tipo: "texto", descricao: "O que é o compromisso. Ex.: Instalar fachada na Padaria do João.", obrigatorio: true, max: 200 },
    data: { tipo: "data", descricao: "O dia do compromisso, de hoje em diante.", obrigatorio: true },
    hora_inicio: { tipo: "hora", descricao: "Horário de início." },
    hora_fim: { tipo: "hora", descricao: "Horário de fim." },
    descricao: { tipo: "texto", descricao: "Observações do compromisso.", max: 2000 },
    cliente_id: { tipo: "id", descricao: "Id do cliente ligado ao compromisso (de buscar_clientes)." },
    endereco: { tipo: "texto", descricao: "Endereço onde acontece.", max: 400 },
    lembrete_minutos: {
      tipo: "inteiro",
      descricao: "Avisar quantos minutos antes. Valores aceitos: 5, 10, 15, 30, 60, 120, 240, 1440 (um dia) e 2880 (dois dias).",
      min: 5,
      max: 2880,
    },
    categoria_id: { tipo: "id", descricao: "Id da categoria da agenda. Sem isto, usa a primeira categoria da empresa." },
  },
  tipo: "escrita",
  permissoes: ["agenda.board.write"],
  async preparar(ctx, args) {
    exigir(ctx, "agenda.board.write", "marcar compromissos");
    const empresaId = ctx.donoDosDados;

    if (args.lembrete_minutos !== undefined && !LEMBRETES_ACEITOS.has(Number(args.lembrete_minutos))) {
      recusar("Lembrete inválido. Use 5, 10, 15, 30, 60, 120, 240, 1440 ou 2880 minutos.");
    }

    /* A categoria é obrigatória na agenda. Sem escolha, vai a primeira que a
       própria empresa criou: as de sistema são das entregas e das contas. */
    let categorias = ctx.supabase
      .from("agenda_categories")
      .select("id,name")
      .eq("company_id", empresaId)
      .eq("is_system", false)
      .order("sort_order")
      .limit(1);
    if (args.categoria_id) categorias = categorias.eq("id", String(args.categoria_id));
    const { data: achadas, error: erroDaCategoria } = await categorias;
    if (erroDaCategoria) falha("conferir a categoria da agenda", erroDaCategoria);
    const categoria = ((achadas ?? []) as Array<{ id: string; name: string | null }>)[0];
    if (!categoria) {
      recusar(
        args.categoria_id
          ? "Categoria não encontrada na agenda desta empresa."
          : "A agenda desta empresa ainda não tem categoria de compromisso. Crie uma no painel, em Agenda, e tente de novo."
      );
    }

    let nomeDoClienteLigado: string | null = null;
    if (args.cliente_id) {
      const { data: cliente } = await ctx.supabase
        .from("clients")
        .select("id,name,company_name")
        .eq("id", String(args.cliente_id))
        .in("user_id", ctx.tenantIds)
        .maybeSingle();
      if (!cliente) recusar("Cliente não encontrado. Use buscar_clientes para achar o id certo.");
      nomeDoClienteLigado = nomeDoCliente(cliente as { name?: string | null; company_name?: string | null });
    }

    const conferido = validar({
      title: args.titulo,
      description: args.descricao,
      event_date: args.data,
      start_time: args.hora_inicio,
      end_time: args.hora_fim,
      client_id: args.cliente_id,
      location_address: args.endereco,
      lembrete_minutos: args.lembrete_minutos,
      category_id: categoria.id,
      status: "agendado",
      recurrence: "none",
    });
    if (!conferido.ok) recusar(conferido.erro);

    const horario = conferido.dados.start_time
      ? conferido.dados.end_time
        ? `${conferido.dados.start_time} às ${conferido.dados.end_time}`
        : `${conferido.dados.start_time}`
      : "dia inteiro";

    return {
      previa: {
        o_que_vai_acontecer: "Um compromisso novo será marcado na agenda da empresa.",
        titulo: conferido.dados.title,
        dia: conferido.dados.event_date,
        horario,
        categoria: categoria.name,
        cliente: nomeDoClienteLigado,
        endereco: conferido.dados.location_address,
        lembrete: conferido.dados.lembrete_minutos ? `${conferido.dados.lembrete_minutos} minutos antes` : "sem lembrete",
        observacoes: conferido.dados.description,
      },
      resumo: `Prévia de compromisso: ${conferido.dados.title} em ${dataBr(conferido.dados.event_date)}`,
      avisos: [],
      carga: { dados: conferido.dados, titulo: conferido.dados.title, dia: conferido.dados.event_date },
    };
  },
  async gravar(ctx, carga) {
    const { data, error } = await ctx.supabase
      .from("agenda_events")
      .insert({ ...carga.dados, company_id: ctx.donoDosDados, created_by: ctx.user.id, responsible_user_id: ctx.user.id })
      .select("id")
      .maybeSingle();
    if (error || !data) falha("marcar o compromisso", error);

    return {
      dados: { criado: true, compromisso_id: (data as { id: string }).id, titulo: carga.titulo, dia: carga.dia },
      resumo: `Marcou o compromisso "${carga.titulo}" em ${dataBr(carga.dia)}`,
    };
  },
};
