"""JWT Bearer token management for YUKKA APIs."""

import base64
import json
from dataclasses import dataclass
from datetime import UTC, datetime, timedelta
from typing import Self

from .exceptions import ExpiredToken, TokenTooOld


@dataclass
class YukkaPayload:
    """Decoded JWT payload from YUKKA authentication.

    Attributes:
        exp: Token expiration timestamp
        iat: Token issued-at timestamp
        email: User email
        groups: List of user groups/permissions
    """

    exp: datetime
    iat: datetime
    email: str
    groups: list[str]


def _parse_jwt(jwt: str) -> YukkaPayload:
    """Parse a JWT token and extract the payload.

    Args:
        jwt: JWT token string

    Returns:
        Decoded payload as YukkaPayload
    """
    # JWT tokens omit base64 padding, but Python's decoder requires it
    payload = jwt.split(".")[1]
    padding = 4 - (len(payload) % 4)
    if padding != 4:
        payload += "=" * padding

    payload_json = json.loads(base64.b64decode(payload))

    return YukkaPayload(
        exp=datetime.fromtimestamp(float(payload_json["exp"]), tz=UTC),
        iat=datetime.fromtimestamp(float(payload_json["iat"]), tz=UTC),
        email=payload_json["email"],
        groups=payload_json["groups"],
    )


@dataclass
class Token:
    """JWT token wrapper.

    Attributes:
        _token: Raw JWT token string
        _payload: Decoded token payload
    """

    _token: str
    _payload: YukkaPayload

    _MAX_TOKEN_AGE_DAYS: int = 120

    @property
    def expired(self) -> bool:
        """Check if the token has expired.

        Returns:
            True if token is expired, False otherwise
        """
        return datetime.now(tz=UTC) > self._payload.exp

    @property
    def too_old(self) -> bool:
        """Check if the token was issued more than 3 months ago.

        Returns:
            True if token is older than 3 months (90 days), False otherwise
        """
        return datetime.now(tz=UTC) - self._payload.iat > timedelta(days=self._MAX_TOKEN_AGE_DAYS)

    def get(self) -> str:
        """Get the current token.

        Returns:
            JWT token string

        Raises:
            TokenTooOld: If token was issued more than 3 months ago
            ExpiredToken: If token is expired
        """
        if self.too_old:
            issued = self._payload.iat.strftime("%Y-%m-%d")
            raise TokenTooOld(
                f"Token was issued on {issued} and is older than {self._MAX_TOKEN_AGE_DAYS} days. "
                "Please obtain a new token."
            )
        if self.expired:
            raise ExpiredToken("Token is expired!")
        return self._token

    @classmethod
    def from_token(cls, token: str) -> Self:
        """Create a Token instance from an existing JWT token.

        Args:
            token: JWT token string

        Returns:
            Token instance
        """
        token = token.strip()
        return cls(_token=token, _payload=_parse_jwt(token))
