"""JWT Token 生成、验证、解析服务"""

from datetime import datetime, timedelta
from typing import Any, Dict, Optional

import jwt
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError


class JWTConfig:
    """JWT 配置"""

    def __init__(
        self,
        secret_key: str,
        algorithm: str = "HS256",
        access_token_expire_minutes: int = 15,
        refresh_token_expire_days: int = 7,
    ):
        """
        初始化 JWT 配置

        Args:
            secret_key: JWT 签名密钥（必须从环境变量或配置注入，不允许默认值）
            algorithm: 加密算法，默认 HS256
            access_token_expire_minutes: Access Token 过期时间（分钟）
            refresh_token_expire_days: Refresh Token 过期时间（天）

        Raises:
            ValueError: 如果 secret_key 为空或包含不安全默认值
        """
        if not secret_key or secret_key.strip() == "":
            raise ValueError("JWT secret_key must be provided and cannot be empty")  # noqa: E501

        insecure_defaults = [
            "your-secret-key-change-in-production",
            "your-secret-key",
            "secret",
            "changeme",
            "password",
        ]
        if secret_key.lower() in [d.lower() for d in insecure_defaults]:
            raise ValueError(  # noqa: E501
                f"JWT secret_key '{secret_key}' is insecure. " "Please provide a strong random secret."
            )

        self.SECRET_KEY: str = secret_key
        self.ALGORITHM: str = algorithm
        self.ACCESS_TOKEN_EXPIRE_MINUTES: int = access_token_expire_minutes
        self.REFRESH_TOKEN_EXPIRE_DAYS: int = refresh_token_expire_days


class TokenType:
    """Token 类型"""

    ACCESS = "access"
    REFRESH = "refresh"


class JWTService:
    """JWT 服务类"""

    def __init__(self, config: JWTConfig = None):
        if config is None:
            raise ValueError(  # noqa: E501
                "JWTService requires a JWTConfig instance. "  # noqa: E501
                "Please provide a configured JWTConfig with a valid "  # noqa: E501
                "secret_key."
            )
        self.config = config

    def create_access_token(
        self,
        user_id: str,
        role: str = "",
        additional_claims: Optional[Dict[str, Any]] = None,
    ) -> str:
        """
        创建 access_token

        Args:
            user_id: 用户 ID
            role: 用户角色（admin/user/guest）
            additional_claims: 额外的声明信息

        Returns:
            JWT Token 字符串
        """
        now = datetime.utcnow()
        expire = now + timedelta(minutes=self.config.ACCESS_TOKEN_EXPIRE_MINUTES)  # noqa: E501

        payload = {
            "sub": user_id,  # subject (用户ID)
            "role": role,
            "type": TokenType.ACCESS,
            "iat": now,  # issued at
            "exp": expire,  # expiration time
        }

        if additional_claims:
            payload.update(additional_claims)

        return jwt.encode(payload, self.config.SECRET_KEY, algorithm=self.config.ALGORITHM)

    def create_refresh_token(self, user_id: str, session_id: str) -> str:
        """
        创建 refresh_token

        Args:
            user_id: 用户 ID
            session_id: Session ID

        Returns:
            JWT Token 字符串
        """
        now = datetime.utcnow()
        expire = now + timedelta(days=self.config.REFRESH_TOKEN_EXPIRE_DAYS)

        payload = {
            "sub": user_id,
            "session_id": session_id,
            "type": TokenType.REFRESH,
            "iat": now,
            "exp": expire,
        }

        return jwt.encode(payload, self.config.SECRET_KEY, algorithm=self.config.ALGORITHM)

    def verify_token(self, token: str) -> Dict[str, Any]:
        """
        验证 Token

        Args:
            token: JWT Token 字符串

        Returns:
            Token payload

        Raises:
            ExpiredSignatureError: Token 已过期
            InvalidTokenError: Token 无效
        """
        try:
            payload = jwt.decode(  # noqa: E501
                token,
                self.config.SECRET_KEY,
                algorithms=[self.config.ALGORITHM],
            )
            return payload
        except ExpiredSignatureError:
            raise ExpiredSignatureError("Token has expired")
        except InvalidTokenError as e:
            raise InvalidTokenError(f"Invalid token: {str(e)}")

    def verify_access_token(self, token: str) -> Dict[str, Any]:
        """
        验证 access_token

        Args:
            token: JWT Token 字符串

        Returns:
            Token payload（如果解码失败返回 None）
        """
        payload = self.verify_token(token)

        if payload.get("type") != TokenType.ACCESS:
            raise ValueError("Token type must be 'access'")

        return payload

    def verify_refresh_token(self, token: str) -> Dict[str, Any]:
        """
        验证 refresh_token

        Args:
            token: JWT Token 字符串

        Returns:
            Token payload

        Raises:
            ValueError: Token 类型不是 refresh
            ExpiredSignatureError: Token 已过期
            InvalidTokenError: Token 无效
        """
        payload = self.verify_token(token)

        if payload.get("type") != TokenType.REFRESH:
            raise ValueError("Token type must be 'refresh'")

        return payload


# 全局实例（生产环境必须从配置读取有效的 secret_key）
# jwt_service = JWTService()  # 不再允许无参数实例化


# Lazy singleton - created with settings on first access
_jwt_service_instance = None


def _get_jwt_service():
    global _jwt_service_instance
    if _jwt_service_instance is None:
        from app.config import settings

        kw = dict(secret_key=settings.JWT_SECRET_KEY)
        if hasattr(settings, "JWT_ALGORITHM"):
            kw["algorithm"] = settings.JWT_ALGORITHM
        if hasattr(settings, "ACCESS_TOKEN_EXPIRE_MINUTES"):
            kw["access_token_expire_minutes"] = settings.ACCESS_TOKEN_EXPIRE_MINUTES
        if hasattr(settings, "REFRESH_TOKEN_EXPIRE_DAYS"):
            kw["refresh_token_expire_days"] = settings.REFRESH_TOKEN_EXPIRE_DAYS
        _jwt_service_instance = JWTService(JWTConfig(**kw))
    return _jwt_service_instance


class _JWTServiceProxy:
    def __getattr__(self, name):
        return getattr(_get_jwt_service(), name)


jwt_service = _JWTServiceProxy()
