/**
 * Affiliate payout rail — Stripe transfers + payouts.
 *
 * Gated behind STRIPE_PAYOUT_ENABLED env var (default 'false').
 * When gate is off, the calling code leaves the payout in status='approved'
 * for manual settlement. Flipping the gate to 'true' activates this module
 * without any code changes.
 *
 * Flow per payout:
 *   1. stripe.transfers.create → funds move from platform to affiliate's IL Express account.
 *   2. stripe.payouts.create   → funds move from Express account to affiliate's IL bank.
 *
 * Idempotency: uses payoutId as the idempotency key prefix for both calls.
 * If either step fails, status stays 'processing' and the admin can retry.
 */

import type Stripe from 'stripe';
import {
  PayoutLedgerMismatchError as ModulePayoutLedgerMismatchError,
  settlePayout,
  sweepStuckPayouts,
} from '@platform-modules/affiliate';
import type { TxDrizzleClient } from '@/server/db/client.js';
import { createAffiliatePayoutExecutor } from '@/server/affiliate-module/payout-executor.js';
import { asPayoutId, toModuleRef } from '@/server/platform-seams/ids.js';

export type PayoutRunResult =
  | { ok: true; transferId: string; payoutId: string }
  | { ok: false; error: string; code: string };

type ModuleMoneyDb = Parameters<typeof settlePayout>[0];

function asModuleMoneyDb(db: TxDrizzleClient): ModuleMoneyDb {
  return db as unknown as ModuleMoneyDb;
}

/**
 * Execute the Stripe transfer + payout for an approved affiliate withdrawal.
 * Live path — delegates to module settlePayout + host PayoutExecutor bridge.
 */
export async function runAffiliatePayout(
  stripe: Stripe,
  db: TxDrizzleClient,
  payoutDbId: string,
  opts: {
    amountAgorot: number;
    stripeAccountId: string;
    enrollmentId: string;
    stripePayoutsEnabled: boolean;
  },
): Promise<PayoutRunResult> {
  if (!opts.stripePayoutsEnabled) {
    return {
      ok: false,
      error: 'Stripe payouts not enabled for this affiliate account',
      code: 'PAYOUT_NOT_ENABLED',
    };
  }

  try {
    const result = await settlePayout(asModuleMoneyDb(db), createAffiliatePayoutExecutor(stripe), {
      payoutId: toModuleRef(asPayoutId(payoutDbId)),
    });

    if (result.ok) {
      return {
        ok: true,
        transferId: result.externalRefs.transferId ?? '',
        payoutId: result.externalRefs.payoutId ?? '',
      };
    }

    return { ok: false, error: result.error, code: result.code };
  } catch (err: unknown) {
    if (err instanceof ModulePayoutLedgerMismatchError) {
      const msg = err instanceof Error ? err.message : 'PAYOUT_LEDGER_MISMATCH';
      return { ok: false, error: msg || 'PAYOUT_LEDGER_MISMATCH', code: 'PAYOUT_LEDGER_MISMATCH' };
    }
    throw err;
  }
}

/** Reclaim affiliate payouts stuck in processing after a worker crash mid-Stripe call. */
export async function sweepStuckAffiliatePayouts(db: TxDrizzleClient): Promise<{ failed: number }> {
  const result = await sweepStuckPayouts(asModuleMoneyDb(db));
  return { failed: result.reclaimed };
}
