from dataclasses import dataclass
from datetime import timedelta
from pathlib import Path
from tomllib import loads
from typing import dataclass_transform

from adaptix import P, Retort, loader
from cryptography.fernet import Fernet

from template_project.application.common.containers import SecretString


@dataclass_transform(frozen_default=True)
def to_configuration[ClsT](cls: type[ClsT]) -> type[ClsT]:
    return dataclass(frozen=True, slots=True, repr=False)(cls)


@to_configuration
class DatabaseConfiguration:
    url: SecretString


@to_configuration
class S3Config:
    bucket_name: str
    endpoint_url: str
    access_key: str
    secret_key: str


@to_configuration
class MlApiConfiguration:
    url: str


@to_configuration
class AccessTokenConfiguration:
    crypto_key: str
    expires_in: timedelta

    def __post_init__(self) -> None:
        try:
            Fernet(self.crypto_key)
        except ValueError as error:
            msg = "access_token.crypto_key must be a valid 32-byte url-safe base64 key"
            raise ValueError(msg) from error


@to_configuration
class ServerConfiguration:
    host: str
    port: int
    access_log: bool

    @property
    def url(self) -> str:
        return f"http://{self.host}:{self.port}"


@to_configuration
class YandexOAuthConfiguration:
    client_id: str
    client_secret: SecretString


@to_configuration
class FirebaseConfiguration:
    certificate_path: str


@to_configuration
class Configuration:
    s3: S3Config
    server: ServerConfiguration
    database: DatabaseConfiguration
    access_token: AccessTokenConfiguration
    yandex_oauth: YandexOAuthConfiguration
    firebase: FirebaseConfiguration
    ml_api: MlApiConfiguration


retort = Retort(
    recipe=[
        loader(SecretString, SecretString),
        loader(P[AccessTokenConfiguration].expires_in, lambda value: timedelta(seconds=value)),
    ],
)


def load_configuration(path: Path) -> Configuration:
    with path.open("r", encoding="utf-8") as config:
        data = loads(config.read())

    return retort.load(data, Configuration)
