"""JWT token service for Mantimon TCG.

This module provides functions for creating and verifying JWT tokens
used in the authentication system.

Token Types:
    - Access tokens: Short-lived (30 min default), used for API authentication
    - Refresh tokens: Longer-lived (7 days default), used to obtain new access tokens

Example:
    from app.services.jwt_service import create_access_token, verify_token

    # Create tokens
    access_token = create_access_token(user_id)
    refresh_token, jti = create_refresh_token(user_id)

    # Verify token
    user_id = verify_token(access_token)
    if user_id:
        print(f"Valid token for user {user_id}")
"""

import uuid
from datetime import UTC, datetime, timedelta

from jose import JWTError, jwt

from app.config import settings
from app.schemas.auth import TokenPayload, TokenType


def create_access_token(user_id: uuid.UUID) -> str:
    """Create a short-lived JWT access token.

    Args:
        user_id: The user's UUID to encode in the token.

    Returns:
        Encoded JWT access token string.

    Example:
        token = create_access_token(user.id)
        # Use token in Authorization header: Bearer {token}
    """
    now = datetime.now(UTC)
    expires = now + timedelta(minutes=settings.jwt_expire_minutes)

    payload = {
        "sub": str(user_id),
        "exp": expires,
        "iat": now,
        "type": TokenType.ACCESS.value,
    }

    return jwt.encode(
        payload,
        settings.secret_key.get_secret_value(),
        algorithm=settings.jwt_algorithm,
    )


def create_refresh_token(user_id: uuid.UUID) -> tuple[str, str]:
    """Create a longer-lived JWT refresh token.

    Refresh tokens include a JTI (JWT ID) for tracking and revocation.

    Args:
        user_id: The user's UUID to encode in the token.

    Returns:
        Tuple of (encoded JWT refresh token, JTI string).
        The JTI should be stored in Redis for revocation tracking.

    Example:
        token, jti = create_refresh_token(user.id)
        await token_store.store_refresh_token(user.id, jti, expires_at)
    """
    now = datetime.now(UTC)
    expires = now + timedelta(days=settings.jwt_refresh_expire_days)
    jti = str(uuid.uuid4())

    payload = {
        "sub": str(user_id),
        "exp": expires,
        "iat": now,
        "type": TokenType.REFRESH.value,
        "jti": jti,
    }

    token = jwt.encode(
        payload,
        settings.secret_key.get_secret_value(),
        algorithm=settings.jwt_algorithm,
    )

    return token, jti


def decode_token(token: str) -> TokenPayload | None:
    """Decode and validate a JWT token.

    Args:
        token: The JWT token string to decode.

    Returns:
        TokenPayload if valid, None if invalid or expired.

    Example:
        payload = decode_token(token)
        if payload:
            user_id = UUID(payload.sub)
    """
    try:
        payload_dict = jwt.decode(
            token,
            settings.secret_key.get_secret_value(),
            algorithms=[settings.jwt_algorithm],
        )
        return TokenPayload(**payload_dict)
    except JWTError:
        return None


def verify_access_token(token: str) -> uuid.UUID | None:
    """Verify an access token and extract the user ID.

    Args:
        token: The JWT access token to verify.

    Returns:
        User UUID if valid access token, None otherwise.

    Example:
        user_id = verify_access_token(token)
        if user_id:
            user = await user_service.get_user_by_id(db, user_id)
    """
    payload = decode_token(token)
    if payload is None:
        return None

    if payload.type != TokenType.ACCESS:
        return None

    try:
        return uuid.UUID(payload.sub)
    except ValueError:
        return None


def verify_refresh_token(token: str) -> tuple[uuid.UUID, str] | None:
    """Verify a refresh token and extract user ID and JTI.

    The JTI should be checked against the token store to ensure
    the token hasn't been revoked.

    Args:
        token: The JWT refresh token to verify.

    Returns:
        Tuple of (user UUID, JTI) if valid refresh token, None otherwise.

    Example:
        result = verify_refresh_token(token)
        if result:
            user_id, jti = result
            if await token_store.is_token_valid(user_id, jti):
                # Issue new access token
    """
    payload = decode_token(token)
    if payload is None:
        return None

    if payload.type != TokenType.REFRESH:
        return None

    if payload.jti is None:
        return None

    try:
        user_id = uuid.UUID(payload.sub)
        return user_id, payload.jti
    except ValueError:
        return None


def get_token_expiration_seconds() -> int:
    """Get the access token expiration time in seconds.

    Useful for the expires_in field in token responses.

    Returns:
        Access token lifetime in seconds.
    """
    return settings.jwt_expire_minutes * 60


def get_refresh_token_expiration() -> datetime:
    """Get the expiration datetime for a new refresh token.

    Useful for setting TTL when storing in Redis.

    Returns:
        Datetime when a refresh token created now would expire.
    """
    return datetime.now(UTC) + timedelta(days=settings.jwt_refresh_expire_days)
