# person_step11.py
#
# ICS 33 Spring 2026
# Code Example
#
# This module rewrites our Person class as a dataclass, which automates almost
# everything that we've been doing by hand, so that our class is boiled entirely
# down to its essence.
#
# * A Person has a name (a string) and a birthdate (a date).
# * A Person can be constructed by specifying its name and birthdate.
# * A Person is immutable and hashable.
# * Persons can be compared for equality.
# * Persons can calculate their age as of a particular date.
#
# Of those five features, the last one was the only one we've had to implement
# by hand.  Everything else is code that's been generated for us by Python's
# dataclass feature.

from dataclasses import dataclass
from datetime import date


# Notice that we're passing arguments to the @dataclass decorator.  That's
# one way to configure the code that it generates for us.  In this case,
# we're telling it two things about how we want it to transform our class:
#
# * Make it "frozen", which is to say that we want its fields to be immutable,
#   which (when combined with the fact that dataclasses, by default, provide an
#   equality feature) will also make Person objects hashable.
# * Make it "kw_only", which means that we can only construct it by specifying
#   keyword arguments.


@dataclass(frozen = True, kw_only = True)
class Person:
    name: str
    birthdate: date


    def age(self, as_of_date: date) -> int:
        if self.birthdate > as_of_date:
            raise ValueError(f'Person was not born yet on {as_of_date}')

        years_old = as_of_date.year - self.birthdate.year

        if (self.birthdate.month, self.birthdate.day) >= (as_of_date.month, as_of_date.day):
            years_old -= 1

        return years_old
