"""Independently check the article's authored aggregation fixture. Python 3.9+."""
import json
from collections import Counter
from fractions import Fraction
from pathlib import Path


def calculate(data):
    hands = data['hands']
    order = ['best', 'correct', 'inaccuracy', 'wrong', 'blunder']
    if len({h['id'] for h in hands}) != len(hands):
        raise ValueError('Duplicate hand id')
    decisions = [d for h in hands for d in h['decisions']]
    if not decisions:
        raise ValueError('No graded decisions')
    if any(d['grade'] not in order or type(d['score']) is not int for d in decisions):
        raise ValueError('Invalid authored grade or score')
    grades = Counter(d['grade'] for d in decisions)
    accuracy = Fraction(grades['best'] + grades['correct'], len(decisions))
    average = Fraction(sum(d['score'] for d in decisions), len(decisions))
    # Positive fixture values: nearest integer, half upward as in the app.
    rounded = lambda n: (2 * n.numerator + n.denominator) // (2 * n.denominator)
    worst = [max((d['grade'] for d in h['decisions']), key=order.index) for h in hands]
    return dict(hands=len(hands), decisions=len(decisions), grades=dict(grades),
                accuracy_fraction=str(accuracy), accuracy_display=str(rounded(accuracy*100))+'%',
                average_score_fraction=str(average), average_score_display=rounded(average),
                worst_grade_per_hand=worst)


if __name__ == '__main__':
    base = Path(__file__).resolve().parent
    data = json.loads((base/'fixture.json').read_text(encoding='utf8'))
    result = calculate(data)
    expected = json.loads((base/'expected.json').read_text(encoding='utf8'))
    if result != expected:
        raise SystemExit('FAIL: independently calculated result differs from expected.json')
    # Aggregation should not depend on the order of hands or decisions.
    reversed_data = dict(hands=[dict(h, decisions=list(reversed(h['decisions']))) for h in reversed(data['hands'])])
    reversed_result = calculate(reversed_data)
    reversed_result['worst_grade_per_hand'].reverse()
    if reversed_result != result:
        raise SystemExit('FAIL: order-invariance check')
    print(json.dumps(result, indent=2))
    print('PASS: exact arithmetic and ordering checks; no solver or app required.')
