"""把应用安全能力适配为 FastAPI 认证依赖和 Cookie 行为。"""

import datetime
from datetime import timedelta
from typing import Annotated, Any, Callable, Optional

import jwt
from fastapi import HTTPException, Request, Response, Security, status
from fastapi.security import (
    APIKeyCookie,
    APIKeyHeader,
    APIKeyQuery,
    HTTPBearer,
    OAuth2PasswordBearer,
)

from app.runtime.cache import cached
from app.runtime.log import logger
from app.runtime.settings import RuntimeSettingsCompat
from app.schemas.token import TokenPayload

SuperuserTokenPayloadProvider = Callable[[], TokenPayload]
TokenEncoder = Callable[..., str]
TokenDecoder = Callable[[str | None, str], TokenPayload]
_superuser_token_payload_provider: Optional[SuperuserTokenPayloadProvider] = None
_token_encoder: Optional[TokenEncoder] = None
_token_decoder: Optional[TokenDecoder] = None
JWT_ALGORITHM = "HS256"


# 兼容旧鉴权插件覆盖模块级设置；令牌实际策略仍由已注入 codec 和 runtime 配置提供。
settings = RuntimeSettingsCompat()

oauth2_scheme_manual_error = OAuth2PasswordBearer(
    auto_error=False,
    tokenUrl=f"{settings.API_V1_STR}/login/access-token",
)
resource_token_cookie = APIKeyCookie(
    name=settings.PROJECT_NAME,
    auto_error=False,
    scheme_name="resource_token_cookie",
)
api_token_query = APIKeyQuery(
    name="token",
    auto_error=False,
    scheme_name="api_token_query",
)
api_key_header = APIKeyHeader(
    name="X-API-KEY",
    auto_error=False,
    scheme_name="api_key_header",
)
api_key_query = APIKeyQuery(
    name="apikey",
    auto_error=False,
    scheme_name="api_key_query",
)
openai_bearer_scheme = HTTPBearer(auto_error=False)
anthropic_api_key_header = APIKeyHeader(
    name="x-api-key",
    auto_error=False,
    scheme_name="anthropic_api_key_header",
)


def set_superuser_token_payload_provider(
    provider: SuperuserTokenPayloadProvider,
) -> None:
    """由启动组合根注入 API 密钥认证使用的超级用户载荷来源。"""
    global _superuser_token_payload_provider
    _superuser_token_payload_provider = provider


def configure_token_codec(
    encoder: TokenEncoder,
    decoder: TokenDecoder,
) -> None:
    """由组合根注入框架无关的令牌编码与解码能力。"""
    global _token_encoder, _token_decoder
    _token_encoder = encoder
    _token_decoder = decoder


def _encode_token(**claims: Any) -> str:
    """使用已注入编码器创建令牌，未装配时给出明确错误。"""
    if _token_encoder is None:
        raise RuntimeError("Web 认证令牌编码器尚未配置")
    return _token_encoder(**claims)


def _decode_token(token: str | None, purpose: str) -> TokenPayload:
    """使用已注入解码器验证令牌，未装配时给出明确错误。"""
    if _token_decoder is None:
        raise RuntimeError("Web 认证令牌解码器尚未配置")
    return _token_decoder(token, purpose)


def _get_api_token(
    token_query: Annotated[str | None, Security(api_token_query)] = None,
) -> str | None:
    """从 URL 查询参数读取兼容 API Token。"""
    return token_query


def _get_api_key(
    key_query: Annotated[str | None, Security(api_key_query)] = None,
    key_header: Annotated[str | None, Security(api_key_header)] = None,
) -> str | None:
    """优先从请求头、其次从查询参数读取兼容 API Key。"""
    return key_header or key_query


@cached(maxsize=1, ttl=600)
def _create_superuser_token_payload() -> TokenPayload:
    """使用组合根提供器创建 API 密钥调用的超级用户载荷。"""
    if not _superuser_token_payload_provider:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="认证服务尚未初始化",
        )
    try:
        return _superuser_token_payload_provider()
    except PermissionError as error:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(error) or "用户权限不足",
        ) from error


def set_or_refresh_resource_token_cookie(
    request: Request,
    response: Response,
    payload: TokenPayload,
) -> None:
    """复用匹配的资源令牌，或为当前身份写入新的安全 Cookie。"""
    resource_token = request.cookies.get(settings.PROJECT_NAME)
    if resource_token:
        try:
            decoded = jwt.decode(
                resource_token,
                settings.RESOURCE_SECRET_KEY,
                algorithms=[JWT_ALGORITHM],
            )
            exp = decoded.get("exp")
            if exp:
                remaining_time = datetime.datetime.fromtimestamp(
                    exp,
                    tz=datetime.UTC,
                ) - datetime.datetime.now(datetime.UTC)
                if remaining_time < timedelta(
                    seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS / 3
                ):
                    raise jwt.ExpiredSignatureError
            expected_claims = {
                "sub": str(payload.sub),
                "username": payload.username,
                "super_user": payload.super_user,
                "level": payload.level,
                "purpose": "resource",
            }
            if any(
                decoded.get(claim) != value
                for claim, value in expected_claims.items()
            ):
                raise jwt.InvalidTokenError("资源令牌身份或权限上下文不匹配")
        except jwt.PyJWTError:
            logger.debug("Token error occurred. refreshing token")
        except Exception as error:
            logger.debug(
                f"Unexpected error occurred while decoding token: {error}"
            )
        else:
            return

    resource_token = _encode_token(
        userid=payload.sub,
        username=payload.username or "",
        super_user=payload.super_user,
        expires_delta=timedelta(
            seconds=settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS
        ),
        level=payload.level,
        purpose="resource",
    )
    is_https = (
        request.url.scheme == "https"
        or request.headers.get("x-forwarded-proto", "").lower() == "https"
    )
    response.set_cookie(
        key=settings.PROJECT_NAME,
        value=resource_token,
        httponly=True,
        secure=is_https,
        samesite="lax",
    )


def _decode_or_http_error(
    token: str | None,
    purpose: str,
) -> TokenPayload:
    """把应用层令牌校验错误转换为 HTTP 403。"""
    try:
        return _decode_token(token, purpose)
    except ValueError as error:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=str(error),
        ) from error


def verify_token(
    request: Request,
    response: Response,
    jwt_token: Annotated[
        str | None,
        Security(oauth2_scheme_manual_error),
    ],
    api_key: Annotated[str | None, Security(_get_api_key)],
    api_token: Annotated[str | None, Security(_get_api_token)],
) -> TokenPayload:
    """验证 JWT、API Key 或 API Token，并维护资源 Cookie。"""
    if jwt_token:
        payload = _decode_or_http_error(jwt_token, "authentication")
        set_or_refresh_resource_token_cookie(request, response, payload)
        return payload
    if api_key:
        verify_apikey(api_key)
        return _create_superuser_token_payload()
    if api_token:
        verify_apitoken(api_token)
        return _create_superuser_token_payload()
    raise HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Not authenticated",
        headers={"WWW-Authenticate": "Bearer"},
    )


def verify_resource_token(
    resource_token: Annotated[
        str | None,
        Security(resource_token_cookie),
    ],
) -> TokenPayload:
    """验证 Cookie 中携带的资源访问令牌。"""
    return _decode_or_http_error(resource_token, "resource")


def _verify_key(key: str | None, expected_key: str, key_type: str) -> str:
    """校验受信第三方集成使用的固定 API 凭据。"""
    if not key or key != expected_key:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=f"{key_type} 校验不通过",
        )
    return key


def verify_apitoken(
    token: Annotated[str | None, Security(_get_api_token)],
) -> str:
    """校验 URL 查询参数中的兼容 API Token。"""
    return _verify_key(token, settings.API_TOKEN, "token")


def verify_apikey(
    apikey: Annotated[str | None, Security(_get_api_key)],
) -> str:
    """校验请求头或查询参数中的兼容 API Key。"""
    return _verify_key(apikey, settings.API_TOKEN, "apikey")
