# Path: esteid/constants.py
# class Countries:
#     """Mainly used by SmartID, also phone code"""
# 
#     ESTONIA = "EE"
#     LATVIA = "LV"
#     LITHUANIA = "LT"
# 
#     ALL = (ESTONIA, LATVIA, LITHUANIA)
# 
# Path: esteid/signing/types.py
# class InterimSessionData(PredictableDict):
#     """
#     Wrapper for temporary data stored between container preparation and finalization requests
#     """
# 
#     digest_b64: str  # stores digest as a base64 encoded string, to allow JSON serialization
#     temp_container_file: str
#     temp_signature_file: str
#     timestamp: int
# 
#     @property
#     def digest(self):
#         return base64.b64decode(self.digest_b64)
# 
#     @digest.setter
#     def digest(self, value: bytes):
#         self.digest_b64 = base64.b64encode(value).decode()
# 
# Path: esteid/types.py
# class PredictableDict(dict):
#     """
#     Allows attribute-style access to values of a dict.
# 
#     Define necessary attributes as type annotations on your subclass:
# 
#         class Z(PredictableDict):
#             required_attr: str
#             optional_attr: Optional[str]
# 
#     and you will get nice attribute-style access with type hints.
# 
#     Validate the presence of all required attributes and type-check all attributes with:
# 
#         Z(required_attr="test").is_valid()
# 
#     Subclasses inherit annotated attributes and can override them, just like normal class attributes.
#     """
# 
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.__dict__ = self
# 
#     def is_valid(self, raise_exception=True):
#         for attr_name, attr_type in self._get_annotations().items():
#             # type -> (type, )
#             # Union[T, S] -> U.__args__ == (T, S)
#             valid_types = getattr(attr_type, "__args__", (attr_type,))
# 
#             try:
#                 val = self[attr_name]
#             except KeyError as e:
#                 # No error for optional fields.
#                 if type(None) in valid_types:
#                     # Optional[T] == Union[T, NoneType]
#                     continue
# 
#                 if not raise_exception:
#                     return False
#                 raise ValueError(f"Missing required key {attr_name}") from e
# 
#             if type(val) not in valid_types:
#                 if not raise_exception:
#                     return False
#                 raise ValueError(f"Wrong type {type(val)} for key {attr_name}")
# 
#         return True
# 
#     @classmethod
#     def _get_annotations(cls):
#         """Collects annotations from all parent classes according to inheritance rules."""
#         annotations = {}
#         for klass in reversed(cls.__mro__):
#             overrides = getattr(klass, "__annotations__", None)
#             if overrides:
#                 annotations.update(overrides)
#         return annotations
# 
# Path: esteid/validators.py
# def validate_id_code(id_code, country):
#     try:
#         validator = ID_CODE_VALIDATORS[country]
#     except (KeyError, TypeError) as e:
#         raise InvalidParameter(f"Unsupported country '{country}'", param="country") from e
# 
#     if not validator(id_code):
#         # Find country name from Countries attributes
#         cty = next(name for name, value in iter(Countries.__dict__.items()) if value == country)
#         raise InvalidIdCode(f"ID code '{id_code}' is not valid for {cty.capitalize()}")


from collections import namedtuple
from typing import Optional
from esteid.constants import Countries
from esteid.signing.types import InterimSessionData
from esteid.types import PredictableDict
from esteid.validators import validate_id_code
        "digest",
        "verification_code",
    ],
)

SignStatusResult = namedtuple(
    "SignStatusResult",
    [
        "document_number",
        "signature",
        "signature_algorithm",
        "certificate",  # DER-encoded certificate
        "certificate_level",
    ],
)


class UserInput(PredictableDict):
    id_code: str
    country: Optional[str]

    def is_valid(self, raise_exception=True):
        """
        Raises InvalidIdCode or ValueError
        """
        result = super().is_valid(raise_exception=raise_exception)
        if result:
            if not (self.get("country") and self.country in Countries.ALL):
                self.country = Countries.ESTONIA


# Path: esteid/constants.py
# class Countries:
#     """Mainly used by SmartID, also phone code"""
# 
#     ESTONIA = "EE"
#     LATVIA = "LV"
#     LITHUANIA = "LT"
# 
#     ALL = (ESTONIA, LATVIA, LITHUANIA)
# 
# Path: esteid/signing/types.py
# class InterimSessionData(PredictableDict):
#     """
#     Wrapper for temporary data stored between container preparation and finalization requests
#     """
# 
#     digest_b64: str  # stores digest as a base64 encoded string, to allow JSON serialization
#     temp_container_file: str
#     temp_signature_file: str
#     timestamp: int
# 
#     @property
#     def digest(self):
#         return base64.b64decode(self.digest_b64)
# 
#     @digest.setter
#     def digest(self, value: bytes):
#         self.digest_b64 = base64.b64encode(value).decode()
# 
# Path: esteid/types.py
# class PredictableDict(dict):
#     """
#     Allows attribute-style access to values of a dict.
# 
#     Define necessary attributes as type annotations on your subclass:
# 
#         class Z(PredictableDict):
#             required_attr: str
#             optional_attr: Optional[str]
# 
#     and you will get nice attribute-style access with type hints.
# 
#     Validate the presence of all required attributes and type-check all attributes with:
# 
#         Z(required_attr="test").is_valid()
# 
#     Subclasses inherit annotated attributes and can override them, just like normal class attributes.
#     """
# 
#     def __init__(self, *args, **kwargs):
#         super().__init__(*args, **kwargs)
#         self.__dict__ = self
# 
#     def is_valid(self, raise_exception=True):
#         for attr_name, attr_type in self._get_annotations().items():
#             # type -> (type, )
#             # Union[T, S] -> U.__args__ == (T, S)
#             valid_types = getattr(attr_type, "__args__", (attr_type,))
# 
#             try:
#                 val = self[attr_name]
#             except KeyError as e:
#                 # No error for optional fields.
#                 if type(None) in valid_types:
#                     # Optional[T] == Union[T, NoneType]
#                     continue
# 
#                 if not raise_exception:
#                     return False
#                 raise ValueError(f"Missing required key {attr_name}") from e
# 
#             if type(val) not in valid_types:
#                 if not raise_exception:
#                     return False
#                 raise ValueError(f"Wrong type {type(val)} for key {attr_name}")
# 
#         return True
# 
#     @classmethod
#     def _get_annotations(cls):
#         """Collects annotations from all parent classes according to inheritance rules."""
#         annotations = {}
#         for klass in reversed(cls.__mro__):
#             overrides = getattr(klass, "__annotations__", None)
#             if overrides:
#                 annotations.update(overrides)
#         return annotations
# 
# Path: esteid/validators.py
# def validate_id_code(id_code, country):
#     try:
#         validator = ID_CODE_VALIDATORS[country]
#     except (KeyError, TypeError) as e:
#         raise InvalidParameter(f"Unsupported country '{country}'", param="country") from e
# 
#     if not validator(id_code):
#         # Find country name from Countries attributes
#         cty = next(name for name, value in iter(Countries.__dict__.items()) if value == country)
#         raise InvalidIdCode(f"ID code '{id_code}' is not valid for {cty.capitalize()}")


# Path: esteid/smartid/types.py
from collections import namedtuple
from typing import Optional
from esteid.constants import Countries
from esteid.signing.types import InterimSessionData
from esteid.types import PredictableDict
from esteid.validators import validate_id_code

        "digest",
        "verification_code",
    ],
)

SignStatusResult = namedtuple(
    "SignStatusResult",
    [
        "document_number",
        "signature",
        "signature_algorithm",
        "certificate",  # DER-encoded certificate
        "certificate_level",
    ],
)


class UserInput(PredictableDict):
    id_code: str
    country: Optional[str]

    def is_valid(self, raise_exception=True):
        """
        Raises InvalidIdCode or ValueError
        """
        result = super().is_valid(raise_exception=raise_exception)
        if result:
            if not (self.get("country") and self.country in Countries.ALL):
                self.country = Countries.ESTONIA


            validate_id_code(self.id_code, self.country)