"""Illustrative capped net-loss rebates, not terms of any real promotion."""
from fractions import Fraction
from math import comb


def release(net_result, rate, balance, cap):
    """One window: net_result is gross game payouts minus eligible stakes."""
    net_result, rate, balance, cap = map(Fraction, (net_result, rate, balance, cap))
    if not 0 <= rate <= 1 or balance < 0 or cap < 0:
        raise ValueError("Require rate in [0,1], nonnegative reserve and cap")
    return min(balance, cap, rate * max(-net_result, Fraction(0)))


def toy_period(rounds, rate=Fraction(1, 5), balance=100, cap=100):
    """Fixed funded one-unit 2x bets, p=.485, no fees/topups/stops within window.

    Initial cash equals rounds, so every requested stake is funded even on the
    all-loss path. A rebate is credited once after the last settled wager.
    """
    if isinstance(rounds, bool) or not isinstance(rounds, int) or rounds < 1:
        raise ValueError("rounds must be a positive integer")
    p = Fraction(97, 200)
    expected_net = expected_loss_base = expected_rebate = Fraction(0)
    mass_sum = Fraction(0)
    for wins in range(rounds + 1):
        mass = comb(rounds, wins) * p**wins * (1 - p)**(rounds - wins)
        net = 2*wins - rounds
        mass_sum += mass
        expected_net += mass*net
        expected_loss_base += mass*max(-net, 0)
        expected_rebate += mass*release(net, rate, balance, cap)
    assert mass_sum == 1
    return dict(rounds=rounds, turnover=rounds, expectedGameNet=expected_net,
                expectedEligibleLoss=expected_loss_base,
                expectedRebate=expected_rebate,
                expectedCombinedNet=expected_net+expected_rebate,
                cashReturnPerUnit=(rounds+expected_net+expected_rebate)/rounds)
