"""
Exercise 1: dataclasses with invariants (easy)

STEP 1: Money: a frozen dataclass with amount (int, cents) and currency (str, default "EUR").
        __post_init__ raises ValueError if amount < 0 or currency isn't 3 uppercase letters.
        add(other) returns a new Money and raises ValueError for different currencies.
STEP 2: Order: a dataclass with id (int), lines (list of Money, default empty list via default_factory),
        and total() returning the sum as Money (EUR 0 when empty).
"""

from dataclasses import dataclass, field
from typing import List


class Money:  # TODO: make it a frozen dataclass with validation
    def __init__(self, amount, currency="EUR"):
        self.amount = amount
        self.currency = currency

    def add(self, other):
        return self


class Order:  # TODO: dataclass with default_factory
    def __init__(self, id, lines=[]):
        self.id = id
        self.lines = lines

    def total(self):
        return Money(0)


# --- self-check: do not modify below this line ---
def raises(fn):
    try:
        fn()
        return False
    except (ValueError, Exception) as e:
        return type(e).__name__

checks = []
checks.append(("value equality", Money(500) == Money(500, "EUR"), True))
checks.append(("negative rejected", raises(lambda: Money(-1)), "ValueError"))
checks.append(("bad currency rejected", raises(lambda: Money(1, "eur")), "ValueError"))
checks.append(("frozen", raises(lambda: setattr(Money(1), "amount", 5)), "FrozenInstanceError"))
checks.append(("add", Money(250).add(Money(100)), Money(350)))
checks.append(("mismatch", raises(lambda: Money(1).add(Money(1, "USD"))), "ValueError"))
o1, o2 = Order(1), Order(2)
o1.lines.append(Money(100))
checks.append(("independent default lists", len(o2.lines), 0))
o1.lines.append(Money(250))
checks.append(("total", o1.total(), Money(350)))
checks.append(("empty total", o2.total(), Money(0)))

passed = 0
for label, actual, expected in checks:
    ok = actual == expected
    passed += ok
    print(f"{'PASS' if ok else 'FAIL'}: {label} (got {actual!r}, expected {expected!r})")

print("PASS" if passed == len(checks) else "FAIL")
