"""Reproduce three declared, fixed-card teaching examples. Python 3.10+, stdlib only.
This is not a general tournament ruling engine or a probability simulation.
"""
from pathlib import Path
import json

HERE = Path(__file__).resolve().parent

def calculate(data):
    old, new = data['oldDenomination'], data['newDenomination']
    if (old, new) != (25, 100):
        raise ValueError('This casebook supports only the stated 25-to-100 profile')
    results = []
    for case in data['cases']:
        rows, tickets, seen = [], [], set()
        for p in case['players']:
            if not isinstance(p['oldChips'], int) or p['oldChips'] < 0 or p['higherValue'] < 0 or p['higherValue'] % new:
                raise ValueError('Invalid chip input')
            exchange, remainder = divmod(p['oldChips'] * old, new)
            if len(p['cards']) != remainder // old:
                raise ValueError('One card required for each remaining old chip')
            rows.append({'seat': p['seat'], 'before': p['higherValue'] + p['oldChips'] * old,
                         'retained': p['higherValue'] + exchange * new, 'remainder': remainder,
                         'ordinaryAward': 0, 'protection': 0})
            for c in p['cards']:
                if len(c) != 2 or c[0] not in '23456789TJQKA' or c[1] not in 'cdhs' or c in seen:
                    raise ValueError('Invalid or duplicate card')
                seen.add(c)
                tickets.append((c, p['seat']))
        if len({r['seat'] for r in rows}) != len(rows):
            raise ValueError('Duplicate seat')
        pool = sum(r['remainder'] for r in rows)
        if pool % new:
            raise ValueError('Fractional pooled awards excluded; obtain the event rounding rule')
        needed = pool // new
        ranked = sorted(tickets, key=lambda t: ('23456789TJQKA'.index(t[0][0]), 'cdhs'.index(t[0][1])), reverse=True)
        winners, trace = [], []
        for card, seat in ranked:
            if len(winners) == needed:
                break
            outcome = 'skip: already awarded' if seat in winners else 'award 100'
            trace.append({'card': card, 'seat': seat, 'outcome': outcome})
            if seat not in winners:
                winners.append(seat)
        if len(winners) != needed:
            raise ValueError('Too few distinct eligible players')
        for row in rows:
            row['ordinaryAward'] = new if row['seat'] in winners else 0
            row['protection'] = new if row['before'] > 0 and row['retained'] + row['ordinaryAward'] == 0 else 0
            row['after'] = row['retained'] + row['ordinaryAward'] + row['protection']
        results.append({'id': case['id'], 'label': case['label'], 'pool': pool, 'ordinaryChips': needed,
                        'winners': winners, 'trace': trace, 'players': rows,
                        'totalBefore': sum(r['before'] for r in rows),
                        'totalAfter': sum(r['after'] for r in rows)})
    return {'method': data['scope'], 'cases': results}

if __name__ == '__main__':
    result = calculate(json.loads((HERE / 'inputs.json').read_text(encoding='utf-8')))
    (HERE / 'results.json').write_text(json.dumps(result, indent=2) + '\n', encoding='utf-8', newline='\n')
    print('Generated three fixed-card cases in results.json')
