import { describe, it, expect } from 'vitest'
import {
  computeAffiliateCommission,
  computeCommission,
  computePercentageCommission,
  computeReferralCommission,
  type CommissionPolicy,
  type CommissionContext,
} from './commission.js'

const baseContext: CommissionContext = {
  referrerId: 'referrer-1',
  refereeId: 'referee-1',
}

describe('computeCommission — host CommissionPolicy seam', () => {
  it('flat policy: 5% of sale in minor units', () => {
    const flat5Pct: CommissionPolicy = {
      resolve({ amountPaidMinor }) {
        return computePercentageCommission(amountPaidMinor, 5)
      },
    }

    expect(
      computeCommission(flat5Pct, {
        amountPaidMinor: 10_000n,
        context: baseContext,
      }),
    ).toBe(500n)
  })

  it('tiered policy: rolling sales count selects tier pct', () => {
    const tieredPolicy: CommissionPolicy = {
      resolve({ amountPaidMinor, context }) {
        const sales = context.rollingSalesCount ?? 0
        let pct = 3
        if (sales >= 50) pct = 5
        else if (sales >= 20) pct = 4
        return computePercentageCommission(amountPaidMinor, pct)
      },
    }

    expect(
      computeCommission(tieredPolicy, {
        amountPaidMinor: 10_000n,
        context: { ...baseContext, rollingSalesCount: 10 },
      }),
    ).toBe(300n)

    expect(
      computeCommission(tieredPolicy, {
        amountPaidMinor: 10_000n,
        context: { ...baseContext, rollingSalesCount: 25 },
      }),
    ).toBe(400n)

    expect(
      computeCommission(tieredPolicy, {
        amountPaidMinor: 10_000n,
        context: { ...baseContext, rollingSalesCount: 55 },
      }),
    ).toBe(500n)
  })

  it('zero amount returns 0 regardless of policy', () => {
    const flatPolicy: CommissionPolicy = {
      resolve() {
        return 999n
      },
    }

    expect(
      computeCommission(flatPolicy, {
        amountPaidMinor: 0n,
        context: baseContext,
      }),
    ).toBe(0n)
  })

  it('computePercentageCommission caps pct at 10 and floors', () => {
    expect(computePercentageCommission(10_000n, 12)).toBe(1_000n)
    expect(computePercentageCommission(9_999n, 7)).toBe(699n)
    expect(computePercentageCommission(10_000n, 0)).toBe(0n)
    expect(computePercentageCommission(0n, 5)).toBe(0n)
  })

  it('computeAffiliateCommission handles a fractional tier pct without throwing (bigint floor)', () => {
    // floor(10_000 · 2.5 / 100) = 250 — never BigInt(2.5) RangeError mid-earn.
    expect(computeAffiliateCommission(10_000n, 2.5)).toBe(250n)
    expect(computeAffiliateCommission(9_999n, 7.5)).toBe(749n)
    // fractional pct above the cap still clamps to 10%
    expect(computeAffiliateCommission(10_000n, 12.5)).toBe(1_000n)
    expect(computeAffiliateCommission(10_000n, 5)).toBe(500n)
    expect(computeAffiliateCommission(10_000n, 0)).toBe(0n)
    expect(computeAffiliateCommission(0n, 5)).toBe(0n)
  })

  it('computeReferralCommission handles a fractional referralPct without throwing (bigint floor)', () => {
    expect(computeReferralCommission(10_000n, { referralPct: 2.5 })).toBe(250n)
    expect(computeReferralCommission(9_999n, { referralPct: 2.5 })).toBe(249n)
    expect(computeReferralCommission(10_000n, { referralPct: 12.5 })).toBe(1_000n)
    expect(computeReferralCommission(10_000n, { referralPct: 2 })).toBe(200n)
    expect(computeReferralCommission(10_000n, { referralPct: 0 })).toBe(0n)
    expect(computeReferralCommission(-1n, { referralPct: 2 })).toBe(0n)
  })

  it('computePercentageCommission handles a fractional pct without throwing (bigint floor)', () => {
    // floor(10_000 · 7.5 / 100) = 750 — never BigInt(7.5) RangeError.
    expect(computePercentageCommission(10_000n, 7.5)).toBe(750n)
    // floor(9_999 · 2.5 / 100) = floor(249.975) = 249
    expect(computePercentageCommission(9_999n, 2.5)).toBe(249n)
    // fractional pct above the cap still clamps to capPct
    expect(computePercentageCommission(10_000n, 12.5)).toBe(1_000n)
  })

  it('tiered policy respects 10% cap via computePercentageCommission', () => {
    const overCapPolicy: CommissionPolicy = {
      resolve({ amountPaidMinor }) {
        return computePercentageCommission(amountPaidMinor, 15)
      },
    }

    expect(
      computeCommission(overCapPolicy, {
        amountPaidMinor: 10_000n,
        context: baseContext,
      }),
    ).toBe(1_000n)
  })
})
