// Architecture decision: US platform (acct_1TbsUX) probing IL Express for affiliate
// payout rail. verifyIlExpressSupported() creates a test Express account with country='IL'
// and transfers capability requested. If Stripe returns 'country_unsupported' /
// 'cross_border_payouts_not_supported', the payout rail stays manual (STRIPE_PAYOUT_ENABLED=false).
// Gate result drives STRIPE_PAYOUT_ENABLED at deploy time — flip to 'true' once probe confirms IL works.

import type Stripe from 'stripe';
import { eq } from 'drizzle-orm';
import type { DrizzleClient } from '@/server/db/client.js';
import { affiliateEnrollments } from '@/server/db/schema.js';
import { updateAffiliateEnrollment } from '@/server/db/queries/referrals/referral-writes.js';
import { captureCaught } from '@/server/observability/capture.server';
import type { AffiliateStripeStatus } from '@/lib/enums/affiliate-stripe-status';

// ---------------------------------------------------------------------------
// Status mapping
// ---------------------------------------------------------------------------

export type StripeAccountStatus = 'pending' | 'restricted' | 'active';

/**
 * Map a Stripe account's KYC state to the internal affiliate stripe_status enum.
 */
export function mapStripeStatusFromAccount(account: {
  details_submitted: boolean;
  charges_enabled: boolean;
  payouts_enabled: boolean;
}): StripeAccountStatus {
  if (!account.details_submitted) return 'pending';
  if (!account.payouts_enabled) return 'restricted';
  return 'active';
}

// ---------------------------------------------------------------------------
// D.0 Cross-border verification probe
// ---------------------------------------------------------------------------

/**
 * Probe whether the US platform account can create an IL Express affiliate
 * account with `transfers` capability.
 *
 * This is called ONCE at deploy time (not in the test suite — mock Stripe there).
 * Returns true  → IL Express is supported; safe to flip STRIPE_PAYOUT_ENABLED=true.
 * Returns false → Stripe rejected country='IL'; leave payout rail manual.
 *
 * The probe creates a real (but thin) account and immediately deletes it.
 * If deletion fails, the orphan account is harmless and will auto-close.
 */
export async function verifyIlExpressSupported(stripe: Stripe): Promise<boolean> {
  let accountId: string | null = null;
  try {
    const account = await stripe.accounts.create({
      type: 'express',
      country: 'IL',
      capabilities: {
        transfers: { requested: true },
      },
      tos_acceptance: { service_agreement: 'recipient' },
    });
    accountId = account.id;
    return true;
  } catch (err: unknown) {
    const stripeErr = err as { code?: string; type?: string; message?: string };
    const msg = stripeErr?.message ?? '';
    if (
      stripeErr?.code === 'country_unsupported' ||
      stripeErr?.code === 'cross_border_payouts_not_supported' ||
      msg.includes('country_unsupported') ||
      msg.includes('cross_border_payouts')
    ) {
      return false;
    }
    // Unexpected error — re-throw so the probe is retried.
    throw err;
  } finally {
    // Best-effort cleanup: delete the probe account.
    if (accountId) {
      try {
        await stripe.accounts.del(accountId);
      } catch (cleanupErr) {
        captureCaught(cleanupErr, {
          scope: 'server.payments.connect.affiliate-onboarding.probe-cleanup',
          severity: 'info',
        });
      }
    }
  }
}

// ---------------------------------------------------------------------------
// D.1 Create Express affiliate account
// ---------------------------------------------------------------------------

/**
 * Create a Stripe Connect Express account for a new affiliate.
 * Uses `transfers` capability only (platform-liable model).
 *
 * Called from POST /api/affiliate/connect/onboard after the user is enrolled.
 * Persists stripe_account_id + stripe_status='pending' on the enrollment row.
 */
export async function createAffiliateConnectAccount(
  stripe: Stripe,
  db: DrizzleClient,
  enrollmentId: string,
  email: string,
): Promise<{ accountId: string }> {
  const account = await stripe.accounts.create({
    type: 'express',
    country: 'IL',
    email,
    capabilities: {
      transfers: { requested: true },
    },
    tos_acceptance: { service_agreement: 'recipient' },
    settings: {
      payouts: {
        // Require manual payout schedule — admin controls transfer timing.
        schedule: { interval: 'manual' },
      },
    },
  });

  await updateAffiliateEnrollment(db, enrollmentId, {
    stripeAccountId: account.id,
    stripeStatus: 'pending',
    stripeUpdatedAt: new Date(),
  });

  return { accountId: account.id };
}

// ---------------------------------------------------------------------------
// D.2 Generate Account Link for onboarding redirect
// ---------------------------------------------------------------------------

/**
 * Generate a Stripe Account Link for the affiliate's KYC onboarding flow.
 * The link expires in ~5 minutes; the affiliate must be redirected immediately.
 */
export async function createAffiliateAccountLink(
  stripe: Stripe,
  stripeAccountId: string,
  opts: { returnUrl: string; refreshUrl: string },
): Promise<{ url: string }> {
  const link = await stripe.accountLinks.create({
    account: stripeAccountId,
    refresh_url: opts.refreshUrl,
    return_url: opts.returnUrl,
    type: 'account_onboarding',
  });
  return { url: link.url };
}

// ---------------------------------------------------------------------------
// Webhook sync: account.updated → affiliate_enrollments
// ---------------------------------------------------------------------------

/**
 * Called from the Stripe webhook handler when account.updated fires.
 * Syncs the affiliate's stripe_status + stripe_payouts_enabled columns.
 * No-ops silently when the account ID matches no affiliate enrollment.
 */
export async function syncAffiliateStripeStatus(
  db: DrizzleClient,
  stripeAccountId: string,
  fields: {
    detailsSubmitted: boolean;
    chargesEnabled: boolean;
    payoutsEnabled: boolean;
  },
): Promise<void> {
  const [enrollment] = await db
    .select({ id: affiliateEnrollments.id })
    .from(affiliateEnrollments)
    .where(eq(affiliateEnrollments.stripeAccountId, stripeAccountId))
    .limit(1);

  if (!enrollment) return; // Not an affiliate account — no-op.

  let stripeStatus: AffiliateStripeStatus;
  if (!fields.detailsSubmitted) {
    stripeStatus = 'pending';
  } else if (!fields.payoutsEnabled) {
    stripeStatus = 'restricted';
  } else {
    stripeStatus = 'enabled';
  }

  await updateAffiliateEnrollment(db, enrollment.id, {
    stripeStatus,
    stripePayoutsEnabled: fields.payoutsEnabled,
    stripeUpdatedAt: new Date(),
  });
}
