// apps/web/src/server/referrals/clawback.ts
import type { TxDrizzleClient } from '@/server/db/client';
import {
  applyClawbackInTx,
  type HostRetainedDeps,
  type HostTransaction,
} from '@/server/affiliate-module/host-retained.js';

const hostRetainedDeps: HostRetainedDeps = { now: () => new Date() };

/**
 * Classify a credit_ledger row for clawback purposes.
 *
 * Classification is based on swept_at, NOT on timestamp comparison.
 * Reason: a row with mature_at in the past but swept_at IS NULL is still
 * "pending" because the sweeper hasn't processed it yet. If we compare
 * refund_at > mature_at we introduce a race condition window between
 * mature_at and the next sweep tick.
 *
 * swept_at IS NULL     → pending-side clawback (debits pending_agorot)
 * swept_at IS NOT NULL → matured-side clawback (debits matured_agorot)
 */
export function classifyClawback(row: {
  sweptAt: Date | null;
  matureAt: Date;
}): 'pending' | 'matured' {
  return row.sweptAt === null ? 'pending' : 'matured';
}

export type ClawbackResult = {
  classification: 'pending' | 'matured';
  originalEntryId: string;
  clawbackEntryId: string;
  clawbackAmountAgorot: number;
};

/**
 * Apply a clawback against an original earn entry (module ledger_entries / affiliate_entries).
 *
 * Live path — delegates to host-retained.applyClawbackInTx (FOR UPDATE OF ae,
 * pending/matured split by swept_at, headroom clamp, refund_clawback idempotency key).
 */
export async function applyClawback(
  db: TxDrizzleClient,
  originalLedgerEntryId: string,
  refundAmountAgorot: number,
  refundEventId?: string,
): Promise<ClawbackResult> {
  const result = await applyClawbackInTx(
    db as unknown as HostTransaction,
    hostRetainedDeps,
    originalLedgerEntryId,
    BigInt(refundAmountAgorot),
    refundEventId,
  );
  return {
    classification: result.classification,
    originalEntryId: result.originalEntryId,
    clawbackEntryId: result.clawbackEntryId,
    clawbackAmountAgorot: Number(result.clawbackAmountMinor),
  };
}
