"""Exact ICM holdback worksheet. Python 3.11+, standard library only.
Run beside this file; writes results.json and casebook.csv there.
No sampled tournaments, user data, or app outputs are used.
"""
from fractions import Fraction as F
from itertools import permutations
from pathlib import Path
import csv
import json

ROOT = Path(__file__).resolve().parent
PLAYERS = ['A', 'B', 'C']
STACKS = [60000, 25000, 15000]
PAYOUTS = [1200, 750, 450]
RESERVES = [[0, 0, 0], [300, 0, 0], [300, 100, 0]]


def exact_model(stacks, payouts, reserve):
    n = len(stacks)
    if not (n == len(payouts) == len(reserve) and n >= 2):
        raise ValueError('Matching vectors with at least two players required')
    if any(type(s) is not int or s <= 0 for s in stacks):
        raise ValueError('Positive integer stacks required')
    if any(type(v) is not int or v < 0 for v in payouts + reserve):
        raise ValueError('Nonnegative integer currency units required')
    if sum(payouts) == 0:
        raise ValueError('Positive total prize pool required')
    adjusted = [p - r for p, r in zip(payouts, reserve)]
    if any(a < 0 for a in adjusted):
        raise ValueError('Reserve exceeds corresponding prize')
    if payouts != sorted(payouts, reverse=True) or adjusted != sorted(adjusted, reverse=True):
        raise ValueError('Original and adjusted payouts must be nonincreasing')
    probabilities = [[F(0) for _ in stacks] for _ in stacks]
    orders = []
    for order in permutations(range(n)):
        probability, remaining = F(1), sum(stacks)
        for player in order:
            probability *= F(stacks[player], remaining)
            remaining -= stacks[player]
        orders.append({'order': list(order), 'probability': str(probability)})
        for rank, player in enumerate(order):
            probabilities[player][rank] += probability
    baseline = [sum(prob * pay for prob, pay in zip(row, payouts)) for row in probabilities]
    guaranteed = [sum(prob * pay for prob, pay in zip(row, adjusted)) for row in probabilities]
    contingent = [sum(prob * pay for prob, pay in zip(row, reserve)) for row in probabilities]
    haircut = [value * F(sum(adjusted), sum(payouts)) for value in baseline]
    for i in range(n):
        if guaranteed[i] + contingent[i] != baseline[i]:
            raise RuntimeError('Linearity failed')
    if sum(guaranteed) + sum(reserve) != sum(payouts):
        raise RuntimeError('Pool reconciliation failed')
    return {
        'stacks': stacks, 'payouts': payouts, 'reserve': reserve, 'adjustedPayouts': adjusted,
        'orders': orders, 'finishProbabilities': [[str(v) for v in row] for row in probabilities],
        'baseline': list(map(str, baseline)), 'guaranteed': list(map(str, guaranteed)),
        'expectedContingent': list(map(str, contingent)), 'proportionalHaircut': list(map(str, haircut)),
    }


def main():
    cases = [exact_model(STACKS, PAYOUTS, reserve) for reserve in RESERVES]
    result = {'researchDate': '2026-09-05', 'currency': 'synthetic USD', 'players': PLAYERS,
              'method': 'Exact Malmuth-Harville finish-order enumeration; rational arithmetic',
              'rounding': 'Exact fractions retained. Display values round independently to cents; these are not settlement instructions.',
              'cases': cases}
    (ROOT / 'results.json').write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8', newline='\n')
    with (ROOT / 'casebook.csv').open('w', newline='', encoding='utf-8') as handle:
        writer = csv.writer(handle, lineterminator='\n')
        writer.writerow(['reserve_by_finish', 'player', 'chips', 'baseline_exact_USD',
                         'guaranteed_exact_USD', 'expected_contingent_exact_USD', 'haircut_exact_USD'])
        for case in cases:
            for i, player in enumerate(PLAYERS):
                writer.writerow(['/'.join(map(str, case['reserve'])), player, STACKS[i],
                                 case['baseline'][i], case['guaranteed'][i],
                                 case['expectedContingent'][i], case['proportionalHaircut'][i]])
    print(json.dumps([{key: [round(float(F(v)), 2) for v in case[key]]
                       for key in ['baseline', 'guaranteed', 'expectedContingent', 'proportionalHaircut']}
                      for case in cases], indent=2))


if __name__ == '__main__':
    main()
