import type { SupabaseClient } from "@supabase/supabase-js";

import { avisarVenda } from "@/lib/chats/aviso";
import { avisarVendaAprovada } from "@/lib/chats/push";
import { getLiquidationCompatibilityError, normalizeLiquidationMethod } from "@/lib/liquidation-accounts";
import { createAdminClient } from "@/lib/supabase-server";

/* Registrar um pagamento num pedido. Saiu da ação `registerPayment`
   (app/actions/financial.ts) para que a tela e o assistente passem pelas
   mesmas conferências: saldo devedor, conta compatível com a forma de
   pagamento e o aviso de venda. A permissão é conferida por quem chama. */

export function toCents(value: number) {
  return Math.round((value + Number.EPSILON) * 100);
}

/**
 * O instante gravado em `payment_date`. Com data e hora, é a hora local de
 * quem registrou; só com a data, meio-dia local. `tzOffsetMinutes` segue o
 * `getTimezoneOffset()` do navegador (Brasília = 180).
 */
export function instanteDoPagamento(entrada: {
  paymentDateTime?: string | null;
  paymentDate?: string | null;
  tzOffsetMinutesRaw?: string | null;
}): { ok: true; valor: string } | { ok: false; erro: string } {
  const { paymentDateTime, paymentDate, tzOffsetMinutesRaw } = entrada;
  if (paymentDateTime) {
    const tzOffsetMinutes = Number.parseInt(tzOffsetMinutesRaw ?? "0", 10);
    const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(paymentDateTime);
    if (!match) {
      return { ok: false, erro: "Data e hora do pagamento inválidas" };
    }

    const year = Number(match[1]);
    const month = Number(match[2]);
    const day = Number(match[3]);
    const hour = Number(match[4]);
    const minute = Number(match[5]);
    const second = match[6] ? Number(match[6]) : 0;

    if ([year, month, day, hour, minute, second, tzOffsetMinutes].some((n) => Number.isNaN(n))) {
      return { ok: false, erro: "Data e hora do pagamento inválidas" };
    }

    const utcMs = Date.UTC(year, month - 1, day, hour, minute, second) + tzOffsetMinutes * 60_000;
    return { ok: true, valor: new Date(utcMs).toISOString() };
  }
  if (paymentDate) {
    const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(paymentDate);
    if (!match) {
      return { ok: false, erro: "Data do pagamento inválida" };
    }

    const year = Number(match[1]);
    const month = Number(match[2]);
    const day = Number(match[3]);
    const tzOffsetMinutes = Number.parseInt(tzOffsetMinutesRaw ?? "0", 10);
    const hour = 12;
    const minute = 0;
    const second = 0;

    const utcMs = Date.UTC(year, month - 1, day, hour, minute, second) + tzOffsetMinutes * 60_000;
    return { ok: true, valor: new Date(utcMs).toISOString() };
  }
  return { ok: false, erro: "Data do pagamento inválida" };
}

export type EntradaDoPagamento = {
  orderId: string;
  amount: number;
  paymentMethod: string;
  paymentDateTime: string | null;
  paymentDate: string | null;
  tzOffsetMinutesRaw: string | null;
  notes: string | null;
  bankAccountIdRaw: string | null;
};

export async function registrarPagamentoDoPedido(
  supabase: SupabaseClient,
  quem: { userId: string; tenantIds: string[] },
  entrada: EntradaDoPagamento
): Promise<{ error: string } | { success: true; valor: number }> {
  const { userId, tenantIds } = quem;
  const { orderId, amount, paymentMethod, paymentDateTime, paymentDate, tzOffsetMinutesRaw, notes, bankAccountIdRaw } = entrada;

  if (!orderId || !amount || !paymentMethod || (!paymentDateTime && !paymentDate)) {
    return { error: "Campos obrigatórios faltando" };
  }

  if (amount <= 0) {
    return { error: "O valor deve ser maior que zero" };
  }

  // Verificar saldo pendente
  const { data: order, error: orderError } = await supabase
    .from("orders")
    .select("user_id,total_amount,paid_amount")
    .eq("id", orderId)
    .in("user_id", tenantIds)
    .single();

  if (orderError || !order) {
    return { error: "Pedido não encontrado" };
  }

  const orderTotal = Number(order.total_amount) || 0;
  const orderPaid = Number(order.paid_amount || 0) || 0;
  const pendingAmount = orderTotal - orderPaid;
  const pendingCents = toCents(pendingAmount);
  const amountCents = toCents(amount);

  if (amountCents > pendingCents) {
    const formatBRL = (valueInCents: number) =>
      new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(valueInCents / 100);
    return {
      error: `O valor do pagamento (${formatBRL(amountCents)}) excede o saldo pendente (${formatBRL(pendingCents)})`,
    };
  }

  const instante = instanteDoPagamento({ paymentDateTime, paymentDate, tzOffsetMinutesRaw });
  if (!instante.ok) {
    return { error: instante.erro };
  }
  const paymentDateToStore = instante.valor;

  const bankAccountId = bankAccountIdRaw?.trim() || null;
  const normalizedPaymentMethod = normalizeLiquidationMethod(paymentMethod);

  if (!normalizedPaymentMethod) {
    return { error: "Forma de pagamento inválida." };
  }

  if (!bankAccountId) {
    const { data: activeAccount } = await supabase
      .from("bank_accounts")
      .select("id")
      .in("company_id", tenantIds)
      .eq("active", true)
      .limit(1)
      .maybeSingle();

    if (activeAccount?.id) {
      return {
        error:
          normalizedPaymentMethod === "cash"
            ? "Selecione o caixa físico para registrar pagamentos em dinheiro."
            : "Selecione a conta bancária para registrar pagamentos eletrônicos.",
      };
    }
  } else {
    const { data: bankAccount, error: bankAccountError } = await supabase
      .from("bank_accounts")
      .select("id, active, company_id, type, name, bank_name")
      .eq("id", bankAccountId)
      .in("company_id", tenantIds)
      .maybeSingle();

    if (bankAccountError || !bankAccount) {
      return {
        error: normalizedPaymentMethod === "cash" ? "Caixa físico inválido." : "Conta bancária inválida.",
      };
    }

    const compatibilityError = getLiquidationCompatibilityError(normalizedPaymentMethod, bankAccount);
    if (compatibilityError) {
      return { error: compatibilityError };
    }
  }

  const { error } = await supabase.from("payments").insert({
    user_id: order.user_id,
    order_id: orderId,
    amount: amountCents / 100,
    payment_method: normalizedPaymentMethod,
    payment_date: paymentDateToStore,
    notes: notes || null,
    created_by: userId,
    bank_account_id: bankAccountId,
  });

  if (error) {
    console.error("Error creating payment:", error);
    return { error: error.message || "Erro ao registrar pagamento" };
  }

  /*
    Dinheiro entrou: avisa o celular de quem nao esta na tela e o sino de quem
    esta. Falha de aviso nunca desfaz o pagamento, que ja esta registrado.
  */
  try {
    const admin = createAdminClient();
    const { data: pedido } = await admin
      .from("orders")
      .select("id,number,user_id,client:clients(name)")
      .eq("id", orderId)
      .maybeSingle();

    const dados = pedido as
      | { id: string; number: number | null; user_id: string; client?: { name?: string } | null }
      | null;
    const cliente = Array.isArray(dados?.client) ? dados?.client[0]?.name : dados?.client?.name;

    await avisarVendaAprovada(admin, {
      tenantId: dados?.user_id || order.user_id,
      valor: amountCents / 100,
      metodo: normalizedPaymentMethod,
      numero: dados?.number ?? null,
      cliente: cliente || null,
      orderId,
      origem: "sistema",
    });
    await avisarVenda(dados?.user_id || order.user_id, {
      valor: amountCents / 100,
      metodo: normalizedPaymentMethod,
      numero: dados?.number ?? null,
      cliente: cliente || null,
      orderId,
      origem: "sistema",
    });
  } catch {
    /* Aviso e conveniencia; o registro e o que importa. */
  }

  return { success: true, valor: amountCents / 100 };
}
