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

import { getLiquidationCompatibilityError, normalizeLiquidationMethod } from "@/lib/liquidation-accounts";
import type { createAdminClient } from "@/lib/supabase-server";

/* Contas a pagar: as consultas com tolerância a colunas que faltam e as rotinas
   de recorrência. Saíram de app/api/accounts-payable/route.ts para que a rota
   e o assistente usem o mesmo código. */

export const writableFields = new Set([
  "description",
  "amount",
  "due_date",
  "bank_account_id",
  "category_id",
  "supplier_id",
  "supplier_name",
  "purchase_order_id",
  "notes",
  "is_recurring",
  "recurrence_rule",
  "status",
]);

export const allowedStatuses = new Set(["PENDING", "PAID", "OVERDUE", "CANCELED"]);

export function parseMonthRange(value: string) {
  if (!/^\d{4}-\d{2}$/.test(value)) return null;
  const [yearRaw, monthRaw] = value.split("-");
  const year = Number(yearRaw);
  const month = Number(monthRaw);
  if (!Number.isInteger(year) || !Number.isInteger(month)) return null;
  if (month < 1 || month > 12) return null;
  const start = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0));
  const end = new Date(Date.UTC(year, month, 1, 0, 0, 0));
  return {
    start: start.toISOString().slice(0, 10),
    end: end.toISOString().slice(0, 10),
  };
}

export function normalizePayableStatus(value: unknown) {
  if (typeof value !== "string") return null;
  const upper = value.trim().toUpperCase();
  return allowedStatuses.has(upper) ? upper : null;
}

export function extractMissingAccountsPayableColumn(error: { message?: string | null; details?: string | null; hint?: string | null } | null) {
  const raw = `${error?.message || ""} ${error?.details || ""} ${error?.hint || ""}`;
  const match =
    raw.match(/Could not find the '([^']+)' column of 'accounts_payable'/i) ||
    raw.match(/column\s+["']?accounts_payable\.?([^"'\s]+)["']?\s+does not exist/i) ||
    raw.match(/column\s+["']?([^"'\s]+)["']?\s+does not exist/i);
  return match?.[1]?.trim() || null;
}

export async function listAccountsPayableWithFallback(
  admin: ReturnType<typeof createAdminClient>,
  input: {
    companyId: string;
    monthRange: { start: string; end: string } | null;
    bankAccountId: string;
    categoryId: string;
    supplierId: string;
    supplierQuery: string;
    purchaseOrderId: string;
  }
) {
  const optionalColumns = new Set([
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ]);
  const missingColumns = new Set<string>();
  let selectedColumns = [
    "id",
    "description",
    "amount",
    "due_date",
    "payment_date",
    "status",
    "bank_account_id",
    "category_id",
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ];
  let lastError: { message?: string | null; details?: string | null; hint?: string | null } | null = null;

  for (let attempt = 0; attempt < optionalColumns.size + 1; attempt += 1) {
    let query = admin
      .from("accounts_payable")
      .select(selectedColumns.join(","))
      .eq("company_id", input.companyId);

    if (input.monthRange) {
      query = query.gte("due_date", input.monthRange.start).lt("due_date", input.monthRange.end);
    }
    if (input.bankAccountId) {
      query = query.eq("bank_account_id", input.bankAccountId);
    }
    if (input.categoryId) {
      query = query.eq("category_id", input.categoryId);
    }
    if (input.supplierId && selectedColumns.includes("supplier_id")) {
      query = query.eq("supplier_id", input.supplierId);
    }
    if (input.supplierQuery && selectedColumns.includes("supplier_name")) {
      query = query.ilike("supplier_name", `%${input.supplierQuery}%`);
    }
    if (input.purchaseOrderId && selectedColumns.includes("purchase_order_id")) {
      query = query.eq("purchase_order_id", input.purchaseOrderId);
    }

    const { data, error } = await query.order("due_date", { ascending: true });
    if (!error) {
      return {
        data: (data ?? []) as unknown as Array<Record<string, unknown>>,
        error: null as null,
        missingColumns,
      };
    }

    lastError = error;
    const missingColumn = extractMissingAccountsPayableColumn(error);
    if (!missingColumn || !optionalColumns.has(missingColumn) || missingColumns.has(missingColumn)) {
      return { data: [] as Array<Record<string, unknown>>, error, missingColumns };
    }

    missingColumns.add(missingColumn);
    selectedColumns = selectedColumns.filter((column) => column !== missingColumn);
  }

  return { data: [] as Array<Record<string, unknown>>, error: lastError, missingColumns };
}

export async function getAccountsPayableByIdWithFallback(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  accountId: string
) {
  const optionalColumns = new Set([
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ]);
  const missingColumns = new Set<string>();
  let selectedColumns = [
    "id",
    "description",
    "amount",
    "due_date",
    "payment_date",
    "status",
    "bank_account_id",
    "category_id",
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ];
  let lastError: { message?: string | null; details?: string | null; hint?: string | null } | null = null;

  for (let attempt = 0; attempt < optionalColumns.size + 1; attempt += 1) {
    const { data, error } = await admin
      .from("accounts_payable")
      .select(selectedColumns.join(","))
      .eq("id", accountId)
      .eq("company_id", companyId)
      .maybeSingle();

    if (!error) {
      return {
        data: data as unknown as Record<string, unknown> | null,
        error: null as null,
        missingColumns,
      };
    }

    lastError = error;
    const missingColumn = extractMissingAccountsPayableColumn(error);
    if (!missingColumn || !optionalColumns.has(missingColumn) || missingColumns.has(missingColumn)) {
      return { data: null, error, missingColumns };
    }

    missingColumns.add(missingColumn);
    selectedColumns = selectedColumns.filter((column) => column !== missingColumn);
  }

  return { data: null, error: lastError, missingColumns };
}

export async function updateAccountsPayableWithFallback(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  accountId: string,
  patch: Record<string, unknown>
) {
  const optionalColumns = new Set([
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ]);
  const missingColumns = new Set<string>();
  let selectedColumns = [
    "id",
    "description",
    "amount",
    "due_date",
    "payment_date",
    "status",
    "bank_account_id",
    "category_id",
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ];
  let currentPatch = { ...patch };
  let lastError: { message?: string | null; details?: string | null; hint?: string | null } | null = null;

  for (let attempt = 0; attempt < optionalColumns.size + 1; attempt += 1) {
    const { data, error } = await admin
      .from("accounts_payable")
      .update(currentPatch)
      .eq("id", accountId)
      .eq("company_id", companyId)
      .select(selectedColumns.join(","))
      .maybeSingle();

    if (!error) {
      return {
        data: data as unknown as Record<string, unknown> | null,
        error: null as null,
        missingColumns,
      };
    }

    lastError = error;
    const missingColumn = extractMissingAccountsPayableColumn(error);
    if (!missingColumn || !optionalColumns.has(missingColumn) || missingColumns.has(missingColumn)) {
      return { data: null, error, missingColumns };
    }

    missingColumns.add(missingColumn);
    selectedColumns = selectedColumns.filter((column) => column !== missingColumn);
    const nextPatch = { ...currentPatch };
    delete nextPatch[missingColumn];
    currentPatch = nextPatch;
  }

  return { data: null, error: lastError, missingColumns };
}

export async function insertAccountsPayableWithFallback(
  admin: ReturnType<typeof createAdminClient>,
  payload: Record<string, unknown>
) {
  const optionalColumns = new Set([
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ]);
  const missingColumns = new Set<string>();
  let selectedColumns = [
    "id",
    "description",
    "amount",
    "due_date",
    "payment_date",
    "status",
    "bank_account_id",
    "category_id",
    "supplier_id",
    "supplier_name",
    "purchase_order_id",
    "notes",
    "is_recurring",
    "recurrence_rule",
    "payment_method",
  ];
  let currentPayload = { ...payload };
  let lastError: { message?: string | null; details?: string | null; hint?: string | null; code?: string | null } | null = null;

  for (let attempt = 0; attempt < optionalColumns.size + 1; attempt += 1) {
    const { data, error } = await admin
      .from("accounts_payable")
      .insert(currentPayload)
      .select(selectedColumns.join(","))
      .maybeSingle();

    if (!error) {
      return {
        data: data as unknown as Record<string, unknown> | null,
        error: null as null,
        missingColumns,
      };
    }

    lastError = error;
    const missingColumn = extractMissingAccountsPayableColumn(error);
    if (!missingColumn || !optionalColumns.has(missingColumn) || missingColumns.has(missingColumn)) {
      return { data: null, error, missingColumns };
    }

    missingColumns.add(missingColumn);
    selectedColumns = selectedColumns.filter((column) => column !== missingColumn);
    const nextPayload = { ...currentPayload };
    delete nextPayload[missingColumn];
    currentPayload = nextPayload;
  }

  return { data: null, error: lastError, missingColumns };
}

export async function insertSupplierActivity(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  supplierId: string | null,
  actorUserId: string | null,
  entityId: string,
  action: string,
  description: string,
  payload: Record<string, unknown> = {}
) {
  if (!supplierId) return;
  await admin.from("supplier_activity_log").insert({
    company_id: companyId,
    supplier_id: supplierId,
    entity_type: "payable",
    entity_id: entityId,
    action,
    description,
    payload,
    actor_user_id: actorUserId,
  });
}

export function pickWritablePatch(value: unknown) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
  const record = value as Record<string, unknown>;
  const patch: Record<string, unknown> = {};
  for (const [key, v] of Object.entries(record)) {
    if (writableFields.has(key)) patch[key] = v;
  }
  return patch;
}

export function normalizeAmount(value: unknown) {
  if (typeof value === "number") return value;
  if (typeof value !== "string") return NaN;
  const normalized = value.replace(",", ".");
  return Number(normalized);
}

export function readStringField(value: unknown, fallback = "") {
  return typeof value === "string" ? value : fallback;
}

export function readNullableStringField(value: unknown) {
  return typeof value === "string" ? value : null;
}

export function readNumberField(value: unknown, fallback = 0) {
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
}

export function monthKeyFromIsoDate(value: string) {
  return value.slice(0, 7);
}

export function addMonthsKeepingDay(isoDate: string, months: number) {
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
  if (!match) return isoDate;
  const year = Number(match[1]);
  const month = Number(match[2]);
  const day = Number(match[3]);
  if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
    return isoDate;
  }

  const monthIndex = month - 1 + months;
  const targetYear = year + Math.floor(monthIndex / 12);
  const targetMonthIndex = ((monthIndex % 12) + 12) % 12;
  const lastDay = new Date(Date.UTC(targetYear, targetMonthIndex + 1, 0)).getUTCDate();
  const clampedDay = Math.min(day, lastDay);
  return new Date(Date.UTC(targetYear, targetMonthIndex, clampedDay)).toISOString().slice(0, 10);
}

export function recurrenceSeriesKey(account: {
  description: string | null;
  bank_account_id: string | null;
  category_id: string | null;
  recurrence_rule: string | null;
}) {
  return [
    (account.description ?? "").trim().toLowerCase(),
    account.bank_account_id ?? "",
    account.category_id ?? "",
    (account.recurrence_rule ?? "").trim().toLowerCase(),
  ].join("|");
}

export async function ensureRecurringInstancesUntilMonth(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  targetMonth: string
) {
  const { data, error } = await admin
    .from("accounts_payable")
    .select(
      "id,description,amount,due_date,status,bank_account_id,category_id,is_recurring,recurrence_rule"
    )
    .eq("company_id", companyId)
    .eq("is_recurring", true)
    .neq("status", "CANCELED")
    .order("due_date", { ascending: true });

  if (error || !data?.length) return;

  const series = new Map<string, (typeof data)[number][]>();
  for (const account of data) {
    if (!account.due_date) continue;
    const key = recurrenceSeriesKey(account);
    const current = series.get(key) ?? [];
    current.push(account);
    series.set(key, current);
  }

  for (const entries of series.values()) {
    entries.sort((a, b) => (a.due_date < b.due_date ? -1 : 1));
    const existingByMonth = new Set(entries.map((entry) => monthKeyFromIsoDate(entry.due_date)));
    let last = entries[entries.length - 1];
    let guard = 0;

    while (monthKeyFromIsoDate(last.due_date) < targetMonth && guard < 60) {
      guard += 1;
      const nextDueDate = addMonthsKeepingDay(last.due_date, 1);
      const nextMonth = monthKeyFromIsoDate(nextDueDate);

      if (existingByMonth.has(nextMonth)) {
        const inMonth = entries
          .filter((entry) => monthKeyFromIsoDate(entry.due_date) === nextMonth)
          .sort((a, b) => (a.due_date < b.due_date ? -1 : 1));
        last = inMonth[inMonth.length - 1] ?? last;
        continue;
      }

      const { data: inserted, error: insertError } = await admin
        .from("accounts_payable")
        .insert({
          company_id: companyId,
          description: last.description,
          amount: last.amount,
          due_date: nextDueDate,
          bank_account_id: last.bank_account_id,
          category_id: last.category_id,
          is_recurring: true,
          recurrence_rule: last.recurrence_rule,
          status: "PENDING",
          payment_date: null,
        })
        .select(
          "id,description,amount,due_date,status,bank_account_id,category_id,is_recurring,recurrence_rule"
        )
        .maybeSingle();

      if (insertError || !inserted) break;
      entries.push(inserted);
      existingByMonth.add(nextMonth);
      last = inserted;
    }
  }
}

export async function ensureNextRecurringInstance(
  admin: ReturnType<typeof createAdminClient>,
  companyId: string,
  account: {
    description: string | null;
    amount: number | null;
    due_date: string | null;
    bank_account_id: string | null;
    category_id: string | null;
    recurrence_rule: string | null;
    is_recurring: boolean | null;
  }
) {
  if (!account.is_recurring || !account.due_date) return;
  const nextDueDate = addMonthsKeepingDay(account.due_date, 1);
  const nextMonth = monthKeyFromIsoDate(nextDueDate);
  const monthRange = parseMonthRange(nextMonth);
  if (!monthRange) return;

  let nextMonthQuery = admin
    .from("accounts_payable")
    .select("id")
    .eq("company_id", companyId)
    .eq("is_recurring", true)
    .eq("description", account.description)
    .gte("due_date", monthRange.start)
    .lt("due_date", monthRange.end)
    .limit(1);

  if (account.bank_account_id) {
    nextMonthQuery = nextMonthQuery.eq("bank_account_id", account.bank_account_id);
  } else {
    nextMonthQuery = nextMonthQuery.is("bank_account_id", null);
  }

  if (account.category_id) {
    nextMonthQuery = nextMonthQuery.eq("category_id", account.category_id);
  } else {
    nextMonthQuery = nextMonthQuery.is("category_id", null);
  }

  if (account.recurrence_rule) {
    nextMonthQuery = nextMonthQuery.eq("recurrence_rule", account.recurrence_rule);
  } else {
    nextMonthQuery = nextMonthQuery.is("recurrence_rule", null);
  }

  const { data: existingMonth, error: existingError } = await nextMonthQuery.maybeSingle();

  if (existingError || existingMonth) return;

  await admin.from("accounts_payable").insert({
    company_id: companyId,
    description: account.description,
    amount: account.amount,
    due_date: nextDueDate,
    bank_account_id: account.bank_account_id,
    category_id: account.category_id,
    is_recurring: true,
    recurrence_rule: account.recurrence_rule,
    status: "PENDING",
    payment_date: null,
  });
}

/* ------------------------------------------------------------ criar e baixar */

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

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

type ClienteDoBanco = SupabaseClient;

/** Cria a conta a pagar: o mesmo que o POST da rota, com o corpo já lido. */
export async function criarContaAPagar(params: {
  supabase: ClienteDoBanco;
  admin: ClienteDoBanco;
  companyId: string;
  userId: string;
  body: {
    description?: unknown;
    amount?: unknown;
    due_date?: unknown;
    bank_account_id?: unknown;
    category_id?: unknown;
    supplier_id?: unknown;
    purchase_order_id?: unknown;
    notes?: unknown;
    is_recurring?: unknown;
    recurrence_rule?: unknown;
    payment_method?: unknown;
  } | null;
}): Promise<ResultadoDaConta> {
  const { supabase, admin, companyId, userId, body } = params;
  const description = typeof body?.description === "string" ? body.description.trim() : "";
  const amount = normalizeAmount(body?.amount);
  const dueDate = typeof body?.due_date === "string" ? body.due_date.trim() : "";
  const bankAccountId =
    typeof body?.bank_account_id === "string" ? body.bank_account_id.trim() : "";

  if (!description) {
    return falhou(400, { error: "Descrição é obrigatória." });
  }

  if (!Number.isFinite(amount) || amount <= 0) {
    return falhou(400, { error: "Valor inválido." });
  }

  if (!/^\d{4}-\d{2}-\d{2}$/.test(dueDate)) {
    return falhou(400, { error: "Data de vencimento inválida." });
  }

  if (!bankAccountId) {
    return falhou(400, { error: "Conta bancária é obrigatória." });
  }

  const categoryId =
    typeof body?.category_id === "string" && body.category_id.trim()
      ? body.category_id.trim()
      : null;
  const supplierId =
    typeof body?.supplier_id === "string" && body.supplier_id.trim()
      ? body.supplier_id.trim()
      : null;
  const purchaseOrderId =
    typeof body?.purchase_order_id === "string" && body.purchase_order_id.trim()
      ? body.purchase_order_id.trim()
      : null;
  const isRecurring = typeof body?.is_recurring === "boolean" ? body.is_recurring : false;
  const recurrenceRule =
    typeof body?.recurrence_rule === "string" && body.recurrence_rule.trim()
      ? body.recurrence_rule.trim()
      : null;
  const notes = typeof body?.notes === "string" && body.notes.trim() ? body.notes.trim() : null;

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

  if (bankAccountError) {
    return falhou(500, { error: "Falha ao validar conta bancária." });
  }

  if (!bankAccount) {
    return falhou(400, { error: "Conta bancária inválida." });
  }

  /* Metodo e opcional na criacao: quem so registra a conta ainda nao sabe como
     vai pagar. Mas se veio informado, a conta escolhida tem de ser compativel —
     senao a conta nasceria impossivel de liquidar, e o erro so apareceria na
     hora de pagar. A tela ja filtra; aqui e a garantia. */
  const createPaymentMethod = normalizeLiquidationMethod(body?.payment_method);
  if (createPaymentMethod) {
    const { data: fullAccount } = await supabase
      .from("bank_accounts")
      .select("id,active,type,name,bank_name")
      .eq("id", bankAccountId)
      .eq("company_id", companyId)
      .maybeSingle();

    const incompativel = getLiquidationCompatibilityError(createPaymentMethod, fullAccount);
    if (incompativel) {
      return falhou(400, { error: incompativel });
    }
  }

  let supplierName: string | null = null;
  if (supplierId) {
    const { data: supplier, error: supplierError } = await admin
      .from("suppliers")
      .select("id,name")
      .eq("id", supplierId)
      .eq("company_id", companyId)
      .is("deleted_at", null)
      .maybeSingle();

    if (supplierError) {
      return falhou(500, { error: "Falha ao validar fornecedor." });
    }

    if (!supplier?.id) {
      return falhou(400, { error: "Fornecedor inválido." });
    }

    supplierName = typeof supplier.name === "string" ? supplier.name : null;
  }

  if (purchaseOrderId) {
    const { data: purchaseOrder, error: purchaseOrderError } = await admin
      .from("supplier_purchase_orders")
      .select("id,supplier_id")
      .eq("id", purchaseOrderId)
      .eq("company_id", companyId)
      .maybeSingle();

    if (purchaseOrderError) {
      return falhou(500, { error: "Falha ao validar compra vinculada." });
    }

    if (!purchaseOrder?.id) {
      return falhou(400, { error: "Compra vinculada inválida." });
    }

    if (supplierId && purchaseOrder.supplier_id && purchaseOrder.supplier_id !== supplierId) {
      return falhou(400, { error: "A compra vinculada pertence a outro fornecedor." });
    }
  }

  // Validar e processar categoria
  let finalCategoryId = categoryId;
  if (categoryId) {
    // Se não for UUID, assume que é nome e busca/cria
    const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(categoryId);
    
    if (!isUuid) {
      // Buscar categoria existente pelo nome
      const { data: existingCategory } = await admin
        .from("financial_categories")
        .select("id")
        .eq("company_id", companyId)
        .ilike("name", categoryId)
        .maybeSingle();
        
      if (existingCategory) {
        finalCategoryId = existingCategory.id;
      } else {
        // Criar nova categoria
        const { data: newCategory, error: createError } = await admin
          .from("financial_categories")
          .insert({
            company_id: companyId,
            name: categoryId,
            icon: "tag"
          })
          .select("id")
          .single();
          
        if (createError) {
          console.error("Erro ao criar categoria automática:", createError);
          return falhou(500, { 
            error: "Falha ao processar categoria.",
            details: createError.message 
          });
        }
        
        finalCategoryId = newCategory.id;
      }
    }
  }

  // 4. Criar conta a pagar com status PENDING
  const { data, error } = await insertAccountsPayableWithFallback(admin, {
    company_id: companyId,
    description,
    amount,
    due_date: dueDate,
    bank_account_id: bankAccountId,
    category_id: finalCategoryId,
    supplier_id: supplierId,
    supplier_name: supplierName,
    purchase_order_id: purchaseOrderId,
    notes,
    is_recurring: isRecurring,
    recurrence_rule: recurrenceRule,
    status: "PENDING",
    payment_date: null,
    payment_method: createPaymentMethod,
  });

  if (error) {
    return falhou(500, 
      {
        error: "Falha ao criar conta a pagar.",
        message: error.message,
        details: error.details,
        hint: error.hint,
        code: error.code,
      });
  }

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

  const createdAccountId = readStringField(data.id);
  if (!createdAccountId) {
    return falhou(500, { error: "Falha ao identificar a conta a pagar criada." });
  }

  await insertSupplierActivity(
    admin,
    companyId,
    supplierId,
    userId,
    createdAccountId,
    "payable.created",
    `Conta a pagar criada: ${description}.`,
    { amount, due_date: dueDate }
  );


  return { ok: true as const, account: data };
}

/** Dá baixa (paga) na conta a pagar: o mesmo que a ação "pay" do PATCH da rota. */
export async function baixarContaAPagar(params: {
  supabase: ClienteDoBanco;
  admin: ClienteDoBanco;
  companyId: string;
  userId: string;
  accountId: string;
  existing: Record<string, unknown>;
  payload: unknown;
}): Promise<ResultadoDaConta> {
  const { supabase, admin, companyId, userId, accountId, existing, payload: payloadBruto } = params;
    const payload = (payloadBruto ?? {}) as Record<string, unknown>;
    const existingId = readStringField(existing.id, accountId);
    const existingDescription = readStringField(existing.description, "Conta a pagar");
    const existingBankAccountId = readNullableStringField(existing.bank_account_id);
    const existingDueDate = readNullableStringField(existing.due_date);
    const existingCategoryId = readNullableStringField(existing.category_id);
    const existingRecurrenceRule = readNullableStringField(existing.recurrence_rule);
    const paymentDateRaw =
      typeof payload.payment_date === "string" ? payload.payment_date.trim() : "";
    const paymentDate = /^\d{4}-\d{2}-\d{2}$/.test(paymentDateRaw)
      ? paymentDateRaw
      : new Date().toISOString().slice(0, 10);
    const bankAccountId =
      typeof payload.bank_account_id === "string" && payload.bank_account_id.trim()
        ? payload.bank_account_id.trim()
        : existingBankAccountId;
    const amount = normalizeAmount(payload.amount);
    const finalAmount = Number.isFinite(amount) && amount > 0 ? amount : Number(readNumberField(existing.amount));
    const paymentMethod = normalizeLiquidationMethod(payload.payment_method);

    if (!bankAccountId) {
      return falhou(400, 
        {
          error:
            paymentMethod === "cash"
              ? "Caixa físico é obrigatório para pagamentos em dinheiro."
              : "Conta bancária é obrigatória para pagamentos eletrônicos.",
        });
    }

    if (!paymentMethod) {
      return falhou(400, { error: "Forma de pagamento inválida." });
    }

    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 inválido." : "Conta bancária inválida.",
        });
    }

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

    const origin = "PAGAR";

    /* Já existe saída deste pagamento que não foi estornada? Pagar, desfazer
       e pagar de novo deixa uma saída antiga + um estorno: aí a saída nova
       precisa entrar (antes a saída antiga fazia a nova ser pulada e a conta
       ficava paga sem tirar o dinheiro do caixa). */
    const [{ data: saidas }, { data: estornos }] = await Promise.all([
      admin.from("cash_flow").select("id").eq("company_id", companyId).eq("origin", origin).eq("type", "OUT").eq("reference_id", existingId),
      admin
        .from("cash_flow")
        .select("id")
        .eq("company_id", companyId)
        .eq("origin", "AJUSTE")
        .eq("type", "IN")
        .eq("reference_id", existingId)
        .like("description", "[PAGAR:REVERSAO]%"),
    ]);
    const existingFlow = (saidas?.length ?? 0) > (estornos?.length ?? 0);

    if (existing.status === "PAID") {
      return { ok: true as const, account: existing };
    }

    const { data: updated, error: updateError } = await updateAccountsPayableWithFallback(admin, companyId, accountId, {
      status: "PAID",
      payment_date: paymentDate,
      bank_account_id: bankAccountId,
      amount: finalAmount,
      payment_method: paymentMethod,
    });

    if (updateError) {
      return falhou(500, { error: "Falha ao atualizar conta a pagar." });
    }

    if (!updated) {
      return falhou(500, { error: "Falha ao atualizar conta a pagar." });
    }

    await insertSupplierActivity(
      admin,
      companyId,
      typeof existing.supplier_id === "string" ? existing.supplier_id : null,
      userId,
      existingId,
      "payable.paid",
      `Conta a pagar baixada: ${existingDescription}.`,
      { payment_date: paymentDate, amount: finalAmount }
    );

    if (!existingFlow) {
      const { error: flowError } = await admin.from("cash_flow").insert({
        company_id: companyId,
        bank_account_id: bankAccountId,
        type: "OUT",
        origin,
        reference_id: existingId,
        description: `Pagamento: ${existingDescription}`,
        amount: finalAmount,
        date: paymentDate,
        created_at: new Date().toISOString(),
      });

      if (flowError) {
        return falhou(500, { error: "Falha ao registrar fluxo de caixa." });
      }
    }

    await ensureNextRecurringInstance(admin, companyId, {
      description: readNullableStringField(updated.description) ?? existingDescription,
      amount: readNumberField(updated.amount, finalAmount),
      due_date: readNullableStringField(updated.due_date) ?? existingDueDate,
      bank_account_id: readNullableStringField(updated.bank_account_id) ?? existingBankAccountId,
      category_id: readNullableStringField(updated.category_id) ?? existingCategoryId,
      recurrence_rule: readNullableStringField(updated.recurrence_rule) ?? existingRecurrenceRule,
      is_recurring: Boolean(updated.is_recurring ?? existing.is_recurring),
    });

    return { ok: true as const, account: updated };
}
