"""Synthetic traffic-mix reversal. Standard library only; no model calls."""
from fractions import Fraction as F
import sys
import unittest

BASELINE = {"routine": (90, 100), "complex": (360, 900)}
CANDIDATE = {"routine": (720, 900), "complex": (30, 100)}
REFERENCE = {"routine": F(1, 10), "complex": F(9, 10)}


def rates(counts):
    if not counts:
        raise ValueError("empty inventory")
    result = {}
    for name, (passed, total) in counts.items():
        if type(passed) is not int or type(total) is not int:
            raise ValueError("counts must be integers, not booleans")
        if total <= 0 or not 0 <= passed <= total:
            raise ValueError("missing or invalid slice evidence")
        result[name] = F(passed, total)
    return result


def observed(counts):
    rates(counts)
    return F(sum(p for p, n in counts.values()), sum(n for p, n in counts.values()))


def standardised(counts, weights):
    r = rates(counts)
    if set(r) != set(weights):
        raise ValueError("slice inventory differs from reference")
    if any(type(w) is not F or w <= 0 for w in weights.values()):
        raise ValueError("use positive Fraction weights")
    if sum(weights.values()) != 1:
        raise ValueError("weights must sum to one")
    return sum(weights[k] * r[k] for k in r)


class Tests(unittest.TestCase):
    def test_observed_reversal(self):
        self.assertEqual(observed(BASELINE), F(45, 100))
        self.assertEqual(observed(CANDIDATE), F(75, 100))
    def test_each_slice_worsens(self):
        a, b = rates(BASELINE), rates(CANDIDATE)
        self.assertTrue(all(b[k] < a[k] for k in a))
    def test_reference_mix(self):
        self.assertEqual(standardised(BASELINE, REFERENCE), F(45, 100))
        self.assertEqual(standardised(CANDIDATE, REFERENCE), F(35, 100))
    def test_equal_mix(self):
        w = {k: F(1, 2) for k in BASELINE}
        self.assertEqual(standardised(BASELINE, w), F(65, 100))
        self.assertEqual(standardised(CANDIDATE, w), F(55, 100))
    def test_reordering(self):
        self.assertEqual(standardised(dict(reversed(list(CANDIDATE.items()))), REFERENCE), F(35, 100))
    def test_zero_denominator(self):
        with self.assertRaises(ValueError):
            rates({"routine": (0, 0)})
    def test_missing_slice(self):
        with self.assertRaises(ValueError):
            standardised({"routine": (9, 10)}, REFERENCE)
    def test_bad_counts(self):
        for row in [(11, 10), (-1, 10), (True, 10), (1, -10), (1.0, 10)]:
            with self.subTest(row=row), self.assertRaises(ValueError):
                rates({"x": row})
    def test_bad_weights(self):
        for weights in [{"routine": F(1), "complex": F(1)}, {"routine": F(0), "complex": F(1)}, {"routine": .1, "complex": .9}]:
            with self.subTest(weights=weights), self.assertRaises(ValueError):
                standardised(BASELINE, weights)
    def test_empty(self):
        with self.assertRaises(ValueError):
            observed({})


def demo():
    for name, data in [("baseline", BASELINE), ("candidate", CANDIDATE)]:
        print(f"{name}: observed={float(observed(data)):.1%}, reference_mix={float(standardised(data, REFERENCE)):.1%}")
        for group, rate in rates(data).items():
            print(f"  {group}: {data[group][0]}/{data[group][1]} = {float(rate):.1%}")


if __name__ == "__main__":
    if sys.argv[1:] == ["--test"]:
        unittest.main(argv=[sys.argv[0]])
    elif sys.argv[1:]:
        raise SystemExit("usage: python3 agent-eval-traffic-mix.py [--test]")
    else:
        demo()
