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

import { getLiquidationCompatibilityError, normalizeLiquidationMethod } from "@/lib/liquidation-accounts";

/* Contas a receber (títulos avulsos): criar e dar baixa. Saíram das rotas
   app/api/accounts-receivable para que a tela e o assistente usem o mesmo
   código. A permissão é conferida por quem chama. */

export const COLUNAS_DA_CONTA_A_RECEBER =
  "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id";

export type ResultadoDoRecebivel =
  | { ok: true; account: Record<string, unknown> | null }
  | { ok: false; status: number; corpo: Record<string, unknown> };

function falhou(status: number, corpo: Record<string, unknown>) {
  return { ok: false as const, status, corpo };
}

function normalizeAmount(value: unknown) {
  const num = Number(value);
  return Number.isFinite(num) ? Math.round(num * 100) / 100 : NaN;
}

function normalizeDate(value: unknown) {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  return /^\d{4}-\d{2}-\d{2}$/.test(trimmed) ? trimmed : null;
}

/** O mesmo que o POST de /api/accounts-receivable, com o corpo já lido. */
export async function criarContaAReceber(params: {
  admin: SupabaseClient;
  companyId: string;
  userId: string;
  body: Record<string, unknown> | null;
}): Promise<ResultadoDoRecebivel> {
  const { admin, companyId, userId, body } = params;
  const description = typeof body?.description === "string" ? body.description.trim() : "";
  const amount = normalizeAmount(body?.total_amount);
  const dueDate = normalizeDate(body?.due_date);
  const clientName = typeof body?.client_name === "string" ? body.client_name.trim() : null;
  const clientCompany = typeof body?.client_company === "string" ? body.client_company.trim() : null;
  const bankAccountId = typeof body?.bank_account_id === "string" && body.bank_account_id.trim() ? body.bank_account_id.trim() : null;
  const categoryId = typeof body?.category_id === "string" && body.category_id.trim() ? body.category_id.trim() : null;
  const notes = typeof body?.notes === "string" ? body.notes.trim() : null;

  if (!description) return falhou(400, { error: "Descrição é obrigatória." });
  if (!Number.isFinite(amount) || amount <= 0) return falhou(400, { error: "Valor inválido." });
  if (!dueDate) return falhou(400, { error: "Vencimento inválido." });

  const { data, error } = await admin
    .from("accounts_receivable")
    .insert({
      company_id: companyId,
      description,
      client_name: clientName || null,
      client_company: clientCompany || null,
      total_amount: amount,
      due_date: dueDate,
      bank_account_id: bankAccountId,
      category_id: categoryId,
      notes,
      status: "PENDING",
      created_by: userId,
    })
    .select(COLUNAS_DA_CONTA_A_RECEBER)
    .maybeSingle();

  if (error || !data) return falhou(500, { error: "Falha ao criar conta a receber." });

  return { ok: true, account: data as unknown as Record<string, unknown> };
}

/**
 * Registra um recebimento no título: o mesmo que POST
 * /api/accounts-receivable/[id]/receive. A função do banco grava o
 * recebimento e a entrada no caixa na mesma transação.
 */
export async function registrarRecebimento(params: {
  supabase: SupabaseClient;
  admin: SupabaseClient;
  companyId: string;
  userId: string;
  receivableId: string;
  body: Record<string, unknown> | null;
}): Promise<ResultadoDoRecebivel> {
  const { supabase, admin, companyId, userId, receivableId: id, body } = params;
  const amount = normalizeAmount(body?.amount);
  const receivedAt = normalizeDate(body?.received_at) ?? new Date().toISOString().slice(0, 10);
  const bankAccountId =
    typeof body?.bank_account_id === "string" && body.bank_account_id.trim() ? body.bank_account_id.trim() : null;
  const paymentMethod = normalizeLiquidationMethod(body?.payment_method);
  const notes = typeof body?.notes === "string" ? body.notes.trim() : null;

  if (!Number.isFinite(amount) || amount <= 0) {
    return falhou(400, { error: "Valor inválido." });
  }
  if (!paymentMethod) {
    return falhou(400, { error: "Forma de pagamento inválida." });
  }
  if (!bankAccountId) {
    return falhou(400, {
      error:
        paymentMethod === "cash"
          ? "Caixa físico é obrigatório para recebimentos em dinheiro."
          : "Conta bancária é obrigatória para recebimentos eletrônicos.",
    });
  }

  const { data: bankAccount, error: bankAccountError } = await supabase
    .from("bank_accounts")
    .select("id,active,type,name,bank_name")
    .eq("id", bankAccountId)
    .eq("company_id", companyId)
    .maybeSingle();

  if (bankAccountError) {
    return falhou(500, { error: "Falha ao validar conta de liquidação." });
  }
  if (!bankAccount) {
    return falhou(400, {
      error: paymentMethod === "cash" ? "Caixa físico não encontrado." : "Conta bancária não encontrada.",
    });
  }

  const compatibilityError = getLiquidationCompatibilityError(paymentMethod, bankAccount);
  if (compatibilityError) {
    return falhou(400, { error: compatibilityError });
  }

  const { error: registerError } = await supabase.rpc("register_receipt_with_cash_flow", {
    p_receivable_id: id,
    p_amount: amount,
    p_received_at: receivedAt,
    p_bank_account_id: bankAccountId,
    p_payment_method: paymentMethod,
    p_notes: notes,
    p_created_by: userId,
  });

  if (registerError) {
    const message = registerError.message ?? "Falha ao registrar recebimento.";
    const lowered = message.toLowerCase();
    const status =
      lowered.includes("nao encontrada") || lowered.includes("não encontrada")
        ? 404
        : lowered.includes("inval") ||
            lowered.includes("saldo") ||
            lowered.includes("quitada") ||
            lowered.includes("cancelado") ||
            lowered.includes("conta bancaria") ||
            lowered.includes("conta bancária")
          ? 400
          : 500;
    return falhou(status, { error: message });
  }

  const { data: updated } = await admin
    .from("accounts_receivable")
    .select("id,description,total_amount,paid_amount,due_date,status")
    .eq("id", id)
    .eq("company_id", companyId)
    .maybeSingle();

  return { ok: true, account: (updated as Record<string, unknown> | null) ?? null };
}
