/**
 * Commission engine — host-supplied policy seam.
 *
 * The module computes commission GIVEN a host-provided CommissionPolicy.
 * A typical tier model (resolveAffiliateTierPct / floor(amt·min(pct,10)/100))
 * becomes ONE host CommissionPolicy implementation, not the module's shape.
 *
 * INVARIANT: Vendor payout is NEVER reduced. Platform absorbs the cost.
 * Money = bigint minor units (agorot) throughout — no float.
 */

import type { ReferralSettings } from './settings.js'

export const PLATFORM_FEE_PCT = 10 as const

export const SELF_VENDOR_PURCHASE = 'SELF_VENDOR_PURCHASE' as const

export interface CommissionContext {
  referrerId: string
  refereeId: string
  productId?: string
  rollingSalesCount?: number
}

export interface CommissionPolicy {
  /** Returns commission in minor units given the settled sale + context. */
  resolve(input: { amountPaidMinor: bigint; context: CommissionContext }): bigint
}

export interface ComputeCommissionInput {
  amountPaidMinor: bigint
  context: CommissionContext
}

/**
 * Compute commission by delegating to the host-supplied policy.
 * Zero or negative sale amounts short-circuit to 0 without calling the policy.
 */
export function computeCommission(
  policy: CommissionPolicy,
  input: ComputeCommissionInput,
): bigint {
  if (input.amountPaidMinor <= 0n) return 0n
  const result = policy.resolve(input)
  return result > 0n ? result : 0n
}

/**
 * Reference percentage helper for host CommissionPolicy implementations.
 * floor(amount · min(pct, capPct) / 100) in bigint minor units.
 *
 * Money stays bigint throughout — `pct` is scaled to integer basis-points
 * (×100, rounded to ≤2 decimal places) so a FRACTIONAL pct (e.g. 7.5) computes
 * the floor faithfully instead of throwing `BigInt(7.5) → RangeError`. For an
 * INTEGER pct this is bit-identical to `floor(amt·min(pct,10)/100)`.
 */
export function computePercentageCommission(
  amountPaidMinor: bigint,
  pct: number,
  capPct = 10,
): bigint {
  if (pct <= 0 || amountPaidMinor <= 0n) return 0n
  const cappedBps = BigInt(Math.round(Math.min(pct, capPct) * 100))
  return (amountPaidMinor * cappedBps) / 10_000n
}

/** Compute the platform's gross net from a paid amount (used for invoicing/reporting). */
export function computePlatformNet(
  amountPaidMinor: bigint,
  feePct: number = PLATFORM_FEE_PCT,
): bigint {
  return (amountPaidMinor * BigInt(feePct)) / 100n
}

/**
 * Resolve the affiliate tier pct (% of sale) for a given trailing-30-day purchase count.
 * Tier locked at accrual — call this once per purchase, store the result as resolved_pct.
 */
export function resolveAffiliateTierPct(
  monthlySales: number,
  settings: Pick<
    ReferralSettings,
    'tier1Pct' | 'tier2Pct' | 'tier3Pct' | 'tier2MinSales' | 'tier3MinSales'
  >,
): number {
  if (monthlySales >= settings.tier3MinSales) return settings.tier3Pct
  if (monthlySales >= settings.tier2MinSales) return settings.tier2Pct
  return settings.tier1Pct
}

/**
 * Compute affiliate commission = floor(amountPaid * resolvedTierPct / 100).
 * resolvedTierPct comes from resolveAffiliateTierPct() — it is % of sale.
 */
export function computeAffiliateCommission(
  amountPaidMinor: bigint,
  resolvedTierPct: number,
): bigint {
  return computePercentageCommission(amountPaidMinor, resolvedTierPct, 10)
}

/**
 * Compute referral store credit = floor(amountPaid * referralPct / 100).
 * referralPct from settings (% of sale). Applied to referrer's wallet as store credit.
 */
export function computeReferralCommission(
  amountPaidMinor: bigint,
  settings: Pick<ReferralSettings, 'referralPct'>,
): bigint {
  return computePercentageCommission(amountPaidMinor, settings.referralPct, 10)
}

/**
 * Determine whether a purchase is a self-vendor scenario (§M.10a).
 * Returns true when the referrer's userId matches the vendor's ownerUserId.
 */
export function isSelfVendorPurchase(
  referrerUserId: string,
  vendorOwnerUserId: string,
): boolean {
  return referrerUserId === vendorOwnerUserId
}
