"""
JWT Token 处理模块

提供 access token 和 refresh token 的创建、验证功能。
支持 RSA 密钥对或 HMAC 对称加密。
"""

import uuid
import logging
import os
import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional, List, Dict, Any
from pathlib import Path

from jose import jwt, JWTError
from passlib.context import CryptContext

from src.auth.models import TokenPayload, TokenPair

logger = logging.getLogger(__name__)

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT 配置
JWT_ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.getenv("ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
REFRESH_TOKEN_EXPIRE_DAYS = int(os.getenv("REFRESH_TOKEN_EXPIRE_DAYS", "7"))

# 密钥配置（生产环境必须从环境变量获取）
def _get_secret_key() -> str:
    """从环境变量获取密钥，如果未设置则生成随机密钥（仅用于开发）"""
    secret = os.getenv("JWT_SECRET_KEY")
    if secret:
        return secret

    # 开发环境：生成随机密钥并警告
    if os.getenv("NODE_ENV", "development") == "development":
        logger.warning(
            "JWT_SECRET_KEY not set in environment. "
            "Using auto-generated random key (tokens will not persist across restarts). "
            "Please set JWT_SECRET_KEY in production!"
        )
        return secrets.token_urlsafe(32)

    # 生产环境必须设置密钥
    raise ValueError(
        "JWT_SECRET_KEY must be set in environment variables for production. "
        "Generate a secure key with: python -c 'import secrets; print(secrets.token_hex(32))'"
    )

SECRET_KEY = _get_secret_key()


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证密码"""
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password: str) -> str:
    """生成密码哈希"""
    return pwd_context.hash(password)


def create_access_token(
    user_id: str,
    scopes: Optional[List[str]] = None,
    expires_delta: Optional[timedelta] = None,
    session_id: Optional[str] = None,
    extra_claims: Optional[Dict[str, Any]] = None
) -> str:
    """
    创建 access token

    Args:
        user_id: 用户 ID
        scopes: 权限范围列表
        expires_delta: 自定义过期时间
        extra_claims: 额外的 JWT claims

    Returns:
        JWT access token string
    """
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)

    now = datetime.now(timezone.utc)

    payload = {
        "sub": user_id,
        "exp": int(expire.timestamp()),
        "iat": int(now.timestamp()),
        "type": "access",
        "scopes": scopes or [],
        "jti": str(uuid.uuid4()),
        "sid": session_id,
    }

    if extra_claims:
        payload.update(extra_claims)

    encoded_jwt = jwt.encode(payload, SECRET_KEY, algorithm=JWT_ALGORITHM)
    return encoded_jwt


def create_refresh_token(
    user_id: str,
    expires_delta: Optional[timedelta] = None,
    jti: Optional[str] = None,
    session_id: Optional[str] = None,
    session_family_id: Optional[str] = None,
) -> str:
    """
    创建 refresh token

    Args:
        user_id: 用户 ID
        expires_delta: 自定义过期时间
        jti: JWT ID（用于 token 撤销）

    Returns:
        JWT refresh token string
    """
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)

    now = datetime.now(timezone.utc)
    token_jti = jti or str(uuid.uuid4())

    payload = {
        "sub": user_id,
        "exp": int(expire.timestamp()),
        "iat": int(now.timestamp()),
        "type": "refresh",
        "jti": token_jti,
        "sid": session_id,
        "sfid": session_family_id,
    }

    encoded_jwt = jwt.encode(payload, SECRET_KEY, algorithm=JWT_ALGORITHM)
    return encoded_jwt


def create_token_pair(
    user_id: str,
    scopes: Optional[List[str]] = None,
    access_expires: Optional[timedelta] = None,
    refresh_expires: Optional[timedelta] = None,
    session_id: Optional[str] = None,
    session_family_id: Optional[str] = None,
) -> TokenPair:
    """
    创建 access token 和 refresh token 对

    Args:
        user_id: 用户 ID
        scopes: 权限范围
        access_expires: access token 过期时间
        refresh_expires: refresh token 过期时间

    Returns:
        TokenPair 包含 access_token 和 refresh_token
    """
    access_token = create_access_token(user_id, scopes, access_expires, session_id=session_id)
    refresh_token = create_refresh_token(
        user_id,
        refresh_expires,
        session_id=session_id,
        session_family_id=session_family_id,
    )

    return TokenPair(
        access_token=access_token,
        refresh_token=refresh_token,
        session_id=session_id,
        session_family_id=session_family_id,
    )


async def verify_token(token: str, token_type: str = "access", check_blacklist: bool = True) -> Optional[TokenPayload]:
    """
    验证 JWT token

    Args:
        token: JWT token string
        token_type: 期望的 token 类型 ("access" 或 "refresh")
        check_blacklist: 是否检查黑名单（默认为 True）

    Returns:
        TokenPayload 如果验证成功，否则 None
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[JWT_ALGORITHM])

        # 验证 token 类型
        if payload.get("type") != token_type:
            logger.warning(f"Token type mismatch: expected {token_type}, got {payload.get('type')}")
            return None

        # 验证必要字段
        if "sub" not in payload or "exp" not in payload:
            logger.warning("Token missing required fields")
            return None

        # 检查是否在黑名单中
        if check_blacklist:
            try:
                from src.services.token_blacklist_service import token_blacklist_service
                is_revoked = await token_blacklist_service.is_token_revoked(token)
                if is_revoked:
                    logger.warning(f"Token has been revoked: {payload.get('jti')}")
                    return None
            except Exception as e:
                logger.error(f"Failed to check token blacklist: {e}")
                # 黑名单检查失败，继续验证（避免服务不可用）

        return TokenPayload(**payload)

    except JWTError as e:
        logger.warning(f"JWT verification failed: {e}")
        return None
    except Exception as e:
        logger.error(f"Unexpected error during token verification: {e}")
        return None


def verify_token_sync(token: str, token_type: str = "access") -> Optional[TokenPayload]:
    """
    同步验证 JWT token（不检查黑名单，用于中间件等同步上下文）

    Args:
        token: JWT token string
        token_type: 期望的 token 类型 ("access" 或 "refresh")

    Returns:
        TokenPayload 如果验证成功，否则 None
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[JWT_ALGORITHM])

        # 验证 token 类型
        if payload.get("type") != token_type:
            logger.warning(f"Token type mismatch: expected {token_type}, got {payload.get('type')}")
            return None

        # 验证必要字段
        if "sub" not in payload or "exp" not in payload:
            logger.warning("Token missing required fields")
            return None

        return TokenPayload(**payload)

    except JWTError as e:
        logger.warning(f"JWT verification failed: {e}")
        return None
    except Exception as e:
        logger.error(f"Unexpected error during token verification: {e}")
        return None


def verify_refresh_token(token: str) -> Optional[TokenPayload]:
    """
    验证 refresh token

    Args:
        token: Refresh token string

    Returns:
        TokenPayload 如果验证成功，否则 None
    """
    return verify_token(token, token_type="refresh")


def decode_token_unsafe(token: str) -> Optional[Dict[str, Any]]:
    """
    不解密直接解码 token（用于获取 payload 信息而不验证签名）

    警告: 仅用于获取非敏感信息，不要用于认证！

    Args:
        token: JWT token string

    Returns:
        Payload dict 或 None
    """
    try:
        # 分割 JWT
        parts = token.split(".")
        if len(parts) != 3:
            return None

        # 解码 payload (第二部分)
        import base64
        payload_b64 = parts[1]
        # 添加 padding
        padding = 4 - len(payload_b64) % 4
        if padding != 4:
            payload_b64 += "=" * padding

        payload_json = base64.urlsafe_b64decode(payload_b64)
        return json.loads(payload_json)

    except Exception as e:
        logger.warning(f"Failed to decode token: {e}")
        return None


def get_token_expiry(token: str) -> Optional[datetime]:
    """
    获取 token 的过期时间

    Args:
        token: JWT token string

    Returns:
        过期时间或 None
    """
    payload = decode_token_unsafe(token)
    if payload and "exp" in payload:
        return datetime.fromtimestamp(payload["exp"], tz=timezone.utc)
    return None


def is_token_expired(token: str) -> bool:
    """
    检查 token 是否已过期

    Args:
        token: JWT token string

    Returns:
        如果已过期返回 True
    """
    expiry = get_token_expiry(token)
    if expiry:
        return datetime.now(timezone.utc) > expiry
    return True


# Import json for decode_token_unsafe
import json
