from datetime import timedelta
from uuid import UUID

from jose import JWTError
from jose.jwt import decode, encode

from api.application.protocols.date_time import DateTimeProvider
from api.application.protocols.jwt import JwtTokenProcessor
from api.domain.user.error import UserInvalidCredentialsError
from api.domain.user.model import UserEmail, UserId
from api.infrastructure.auth.jwt_settings import JwtSettings


class JoseJwtTokenProcessor(JwtTokenProcessor):
    def __init__(self, jwt_options: JwtSettings, date_time_provider: DateTimeProvider) -> None:
        self.jwt_options = jwt_options
        self.date_time_provider = date_time_provider

    def generate_token(self, user_id: UserId, user_email: UserEmail) -> str:
        issued_at = self.date_time_provider.get_current_time()
        expiration_time = issued_at + timedelta(minutes=self.jwt_options.expires_in)

        claims = {
            "iat": issued_at,
            "exp": expiration_time,
            "sub": str(user_id.value),
            "email": user_email.value,
        }

        return encode(claims, self.jwt_options.secret, self.jwt_options.algorithm)

    def validate_token(self, token: str) -> tuple[UserId, UserEmail] | None:
        try:
            payload = decode(token, self.jwt_options.secret, [self.jwt_options.algorithm])
            return UserId(UUID(payload["sub"])), UserEmail(payload["email"])
        except (JWTError, ValueError, KeyError):
            return None

    def refresh_token(self, token: str) -> str:
        token_data = self.validate_token(token)
        if token_data is None:
            raise UserInvalidCredentialsError("invalid token")
        return self.generate_token(token_data[0], token_data[1])