import { validateDocument } from "@/lib/document-utils";

import {
  AVISO_DE_DADOS,
  SITUACAO_DO_ORCAMENTO,
  SITUACAO_DO_PEDIDO,
  SITUACAO_FINANCEIRA,
  contagem,
  dinheiro,
  exigir,
  falha,
  limparBusca,
  recusar,
  texto,
} from "./comum";
import type { FerramentaDeEscrita, FerramentaDeLeitura } from "./tipos";

type ClienteDaLista = {
  id: string;
  name: string | null;
  company_name: string | null;
  type: string | null;
  email: string | null;
  phone: string | null;
  whatsapp: string | null;
  address_city: string | null;
  address_state: string | null;
  active: boolean | null;
  created_at: string | null;
};

const COLUNAS_DA_LISTA = "id,name,company_name,type,email,phone,whatsapp,address_city,address_state,active,created_at";

function linhaDoCliente(row: ClienteDaLista) {
  return {
    id: row.id,
    nome: row.name ?? "",
    empresa: row.company_name || null,
    tipo: row.type === "pj" ? "empresa" : "pessoa",
    email: row.email || null,
    telefone: row.phone || null,
    whatsapp: row.whatsapp || null,
    cidade: [row.address_city, row.address_state].filter(Boolean).join(" / ") || null,
    ativo: row.active !== false,
  };
}

const soDigitos = (valor: unknown) => String(valor ?? "").replace(/\D/g, "");

export const buscarClientes: FerramentaDeLeitura = {
  nome: "buscar_clientes",
  titulo: "Buscar clientes",
  descricao:
    "Procura clientes pelo nome, nome da empresa, e-mail, telefone ou CPF/CNPJ e devolve o id de cada um. Use antes de criar um orçamento, para pegar o id do cliente. Sem busca, lista os clientes mais recentes.",
  campos: {
    busca: { tipo: "texto", descricao: "Nome, empresa, e-mail, telefone ou documento do cliente.", max: 80 },
    limite: { tipo: "inteiro", descricao: "Quantos devolver. O padrão é 15.", min: 1, max: 50 },
  },
  tipo: "leitura",
  permissoes: ["clients.list.read"],
  async executar(ctx, args) {
    exigir(ctx, "clients.list.read", "ver os clientes");
    const busca = limparBusca(args.busca);
    const limite = (args.limite as number | undefined) ?? 15;

    let consulta = ctx.supabase
      .from("clients")
      .select(COLUNAS_DA_LISTA)
      .in("user_id", ctx.tenantIds)
      .not("is_deleted", "is", true)
      .not("is_system_placeholder", "is", true)
      .order("updated_at", { ascending: false })
      .limit(limite);

    if (busca) {
      const digitos = soDigitos(busca);
      const filtros = [`name.ilike.%${busca}%`, `company_name.ilike.%${busca}%`, `email.ilike.%${busca}%`];
      /* Telefone e documento são gravados com e sem máscara: procura pelo fim
         do número, que é o trecho que não muda entre os dois jeitos. */
      if (digitos.length >= 4) {
        const fim = digitos.slice(-8);
        filtros.push(`phone.ilike.%${fim}%`, `whatsapp.ilike.%${fim}%`, `document.ilike.%${digitos.slice(-4)}%`);
      }
      consulta = consulta.or(filtros.join(","));
    }

    const { data, error } = await consulta;
    if (error) falha("buscar os clientes", error);
    const clientes = ((data ?? []) as ClienteDaLista[]).map(linhaDoCliente);

    return {
      dados: { _observacao: AVISO_DE_DADOS, clientes, encontrados: clientes.length },
      resumo: busca
        ? `Buscou clientes por "${busca}" (${contagem(clientes.length, "encontrado", "encontrados")})`
        : `Listou os clientes recentes (${clientes.length})`,
    };
  },
};

type OrcamentoDoCliente = { id: string; number: number | null; status: string | null; total: number | null; title: string | null; created_at: string | null };
type PedidoDoCliente = {
  id: string;
  number: number | null;
  status: string | null;
  total_amount: number | null;
  paid_amount: number | null;
  financial_status: string | null;
  created_at: string | null;
  delivery_date: string | null;
};

export const verCliente: FerramentaDeLeitura = {
  nome: "ver_cliente",
  titulo: "Ver um cliente",
  descricao:
    "Mostra o cadastro de um cliente e o histórico dele com a empresa: os últimos orçamentos, os últimos pedidos, quanto já comprou e quanto ainda deve.",
  campos: {
    cliente_id: { tipo: "id", descricao: "Id do cliente, devolvido por buscar_clientes.", obrigatorio: true },
  },
  tipo: "leitura",
  permissoes: ["clients.list.read"],
  async executar(ctx, args) {
    exigir(ctx, "clients.list.read", "ver os clientes");
    const clienteId = String(args.cliente_id);

    const { data, error } = await ctx.supabase
      .from("clients")
      .select(`${COLUNAS_DA_LISTA},document,address_street,address_number,address_neighborhood,address_zip,notes,tags,origin,is_deleted`)
      .eq("id", clienteId)
      .in("user_id", ctx.tenantIds)
      .maybeSingle();
    if (error) falha("ler o cliente", error);
    if (!data || (data as { is_deleted?: boolean | null }).is_deleted) {
      recusar("Cliente não encontrado. Use buscar_clientes para achar o id certo.");
    }
    const cliente = data as unknown as ClienteDaLista & {
      document: string | null;
      address_street: string | null;
      address_number: string | null;
      address_neighborhood: string | null;
      address_zip: string | null;
      notes: string | null;
      tags: string[] | null;
    };

    const [orcamentos, pedidos] = await Promise.all([
      ctx.supabase
        .from("quotes")
        .select("id,number,status,total,title,created_at")
        .eq("client_id", clienteId)
        .in("user_id", ctx.tenantIds)
        .order("created_at", { ascending: false })
        .limit(5),
      ctx.supabase
        .from("orders")
        .select("id,number,status,total_amount,paid_amount,financial_status,created_at,delivery_date")
        .eq("client_id", clienteId)
        .in("user_id", ctx.tenantIds)
        .order("created_at", { ascending: false })
        .limit(200),
    ]);

    const listaDePedidos = ((pedidos.data ?? []) as PedidoDoCliente[]).filter((pedido) => pedido.status !== "canceled");
    const comprou = listaDePedidos.reduce((soma, pedido) => soma + Number(pedido.total_amount || 0), 0);
    const deve = listaDePedidos
      .filter((pedido) => pedido.financial_status !== "paid")
      .reduce((soma, pedido) => soma + (Number(pedido.total_amount || 0) - Number(pedido.paid_amount || 0)), 0);
    const endereco = [cliente.address_street, cliente.address_number, cliente.address_neighborhood].filter(Boolean).join(", ");

    return {
      dados: {
        _observacao: AVISO_DE_DADOS,
        cliente: {
          ...linhaDoCliente(cliente),
          documento: cliente.document || null,
          endereco: endereco || null,
          cep: cliente.address_zip || null,
          observacoes: texto(cliente.notes, 800),
          etiquetas: Array.isArray(cliente.tags) ? cliente.tags : [],
          cadastrado_em: cliente.created_at,
        },
        historico: {
          pedidos: listaDePedidos.length,
          total_comprado: dinheiro(comprou),
          em_aberto: dinheiro(deve),
          ultimos_pedidos: listaDePedidos.slice(0, 5).map((pedido) => ({
            id: pedido.id,
            numero: pedido.number,
            situacao: SITUACAO_DO_PEDIDO[pedido.status ?? ""] ?? pedido.status,
            pagamento: SITUACAO_FINANCEIRA[pedido.financial_status ?? ""] ?? pedido.financial_status,
            total: dinheiro(pedido.total_amount),
            criado_em: pedido.created_at,
            entrega: pedido.delivery_date,
          })),
          ultimos_orcamentos: ((orcamentos.data ?? []) as OrcamentoDoCliente[]).map((orcamento) => ({
            id: orcamento.id,
            numero: orcamento.number,
            situacao: SITUACAO_DO_ORCAMENTO[orcamento.status ?? ""] ?? orcamento.status,
            total: dinheiro(orcamento.total),
            titulo: orcamento.title || null,
            criado_em: orcamento.created_at,
          })),
        },
      },
      resumo: `Consultou o cliente ${cliente.company_name || cliente.name || ""}`.trim(),
    };
  },
};

/* ------------------------------------------------------------ criar cliente */

type NovoCliente = {
  name: string;
  company_name: string | null;
  type: "pf" | "pj";
  document: string | null;
  email: string | null;
  phone: string | null;
  whatsapp: string | null;
  address_city: string | null;
  address_state: string | null;
  notes: string | null;
};

function documentoFormatado(digitos: string) {
  if (digitos.length === 11) return digitos.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
  if (digitos.length === 14) return digitos.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, "$1.$2.$3/$4-$5");
  return digitos;
}

/* O telefone como o painel grava o WhatsApp: +55 e os dígitos com DDD. */
function whatsappNoPadrao(valor: unknown): string | null | "invalido" {
  let digitos = soDigitos(valor);
  if (!digitos) return null;
  if (digitos.startsWith("55") && digitos.length >= 12) digitos = digitos.slice(2);
  if (digitos.length !== 10 && digitos.length !== 11) return "invalido";
  return `+55${digitos}`;
}

export const criarCliente: FerramentaDeEscrita<NovoCliente> = {
  nome: "criar_cliente",
  titulo: "Cadastrar um cliente",
  descricao:
    "Cadastra um cliente novo. Antes, use buscar_clientes para conferir se ele já existe. 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: {
    nome: { tipo: "texto", descricao: "Nome da pessoa (ou do contato, se for empresa).", obrigatorio: true, min: 2, max: 120 },
    empresa: { tipo: "texto", descricao: "Nome da empresa do cliente, se houver.", max: 120 },
    tipo: { tipo: "opcao", descricao: "pessoa (CPF) ou empresa (CNPJ). Sem isto, decide pelo documento.", opcoes: ["pessoa", "empresa"] },
    documento: { tipo: "texto", descricao: "CPF ou CNPJ, com ou sem pontuação.", max: 20 },
    email: { tipo: "texto", descricao: "E-mail do cliente.", max: 120 },
    telefone: { tipo: "texto", descricao: "Telefone fixo ou outro número, com DDD.", max: 20 },
    whatsapp: { tipo: "texto", descricao: "Celular com WhatsApp, com DDD. Ex.: 81999990000.", max: 20 },
    cidade: { tipo: "texto", descricao: "Cidade.", max: 80 },
    estado: { tipo: "texto", descricao: "Sigla do estado, com duas letras. Ex.: PE.", min: 2, max: 2 },
    observacoes: { tipo: "texto", descricao: "Anotação interna sobre o cliente.", max: 800 },
  },
  tipo: "escrita",
  permissoes: ["clients.list.write"],
  async preparar(ctx, args) {
    exigir(ctx, "clients.list.write", "cadastrar clientes");

    const digitosDoDocumento = soDigitos(args.documento);
    const tipo: "pf" | "pj" = args.tipo === "empresa" ? "pj" : args.tipo === "pessoa" ? "pf" : digitosDoDocumento.length === 14 ? "pj" : "pf";
    if (digitosDoDocumento && !validateDocument(digitosDoDocumento, tipo)) {
      recusar(`O ${tipo === "pf" ? "CPF" : "CNPJ"} informado não é válido. Confira os números ou deixe o documento em branco.`);
    }

    const email = String(args.email ?? "").trim().toLowerCase();
    if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) recusar("O e-mail informado não é válido.");

    const whatsapp = whatsappNoPadrao(args.whatsapp);
    if (whatsapp === "invalido") recusar("O WhatsApp precisa ter DDD e número, com 10 ou 11 dígitos. Ex.: 81999990000.");

    const estado = String(args.estado ?? "").trim().toUpperCase();
    if (estado && !/^[A-Z]{2}$/.test(estado)) recusar("O estado precisa ser a sigla com duas letras. Ex.: PE.");

    const carga: NovoCliente = {
      name: String(args.nome),
      company_name: (args.empresa as string | undefined) || null,
      type: tipo,
      document: digitosDoDocumento ? documentoFormatado(digitosDoDocumento) : null,
      email: email || null,
      phone: (args.telefone as string | undefined) || null,
      whatsapp,
      address_city: (args.cidade as string | undefined) || null,
      address_state: estado || null,
      notes: (args.observacoes as string | undefined) || null,
    };

    /* Mesma regra da tela: documento repetido não cadastra de novo. */
    if (digitosDoDocumento) {
      const { data: repetidos } = await ctx.supabase
        .from("clients")
        .select("id,name,company_name")
        .in("user_id", ctx.tenantIds)
        .in("document", [digitosDoDocumento, documentoFormatado(digitosDoDocumento)])
        .not("is_deleted", "is", true)
        .limit(1);
      const repetido = (repetidos ?? [])[0] as { id: string; name: string | null; company_name: string | null } | undefined;
      if (repetido) {
        recusar(
          `Já existe um cliente com este ${tipo === "pf" ? "CPF" : "CNPJ"}: ${repetido.company_name || repetido.name} (id ${repetido.id}). Use esse cadastro em vez de criar outro.`
        );
      }
    }

    /* Telefone ou e-mail igual não impede, mas a pessoa precisa saber. */
    const avisos: string[] = [];
    const filtros: string[] = [];
    const fimDoNumero = soDigitos(whatsapp || args.telefone).slice(-8);
    if (fimDoNumero.length === 8) filtros.push(`phone.ilike.%${fimDoNumero}%`, `whatsapp.ilike.%${fimDoNumero}%`);
    if (email) filtros.push(`email.ilike.${limparBusca(email)}`);
    if (filtros.length) {
      const { data: parecidos } = await ctx.supabase
        .from("clients")
        .select("id,name,company_name")
        .in("user_id", ctx.tenantIds)
        .not("is_deleted", "is", true)
        .not("is_system_placeholder", "is", true)
        .or(filtros.join(","))
        .limit(3);
      for (const parecido of (parecidos ?? []) as Array<{ id: string; name: string | null; company_name: string | null }>) {
        avisos.push(`Já existe um cliente com o mesmo telefone ou e-mail: ${parecido.company_name || parecido.name} (id ${parecido.id}). Confira se não é a mesma pessoa.`);
      }
    }

    return {
      previa: {
        o_que_vai_acontecer: "Um cliente novo será cadastrado.",
        cliente: {
          nome: carga.name,
          empresa: carga.company_name,
          tipo: tipo === "pj" ? "empresa" : "pessoa",
          documento: carga.document,
          email: carga.email,
          telefone: carga.phone,
          whatsapp: carga.whatsapp,
          cidade: [carga.address_city, carga.address_state].filter(Boolean).join(" / ") || null,
          observacoes: carga.notes,
        },
      },
      resumo: `Prévia do cadastro do cliente ${carga.company_name || carga.name}`,
      avisos,
      carga,
    };
  },
  async gravar(ctx, carga) {
    const agora = new Date().toISOString();
    const { data, error } = await ctx.supabase
      .from("clients")
      .insert([
        {
          ...carga,
          tags: [],
          active: true,
          whatsapp_notify: true,
          user_id: ctx.donoDosDados,
          created_at: agora,
          updated_at: agora,
        },
      ])
      .select("id")
      .single();
    if (error || !data) falha("cadastrar o cliente", error);

    return {
      dados: {
        criado: true,
        cliente_id: (data as { id: string }).id,
        nome: carga.company_name || carga.name,
      },
      resumo: `Cadastrou o cliente ${carga.company_name || carga.name}`,
    };
  },
};
