"""gRPC 拦截器.

提供：
  - LoggingInterceptor: 请求/响应日志 + 延迟统计
  - ErrorInterceptor: 异常捕获 → gRPC status code 映射
  - AuthInterceptor: 从 metadata 提取用户上下文
"""

import time
from collections.abc import Callable
from typing import Any

import grpc
import structlog

from ..errors import AIError
from ..middleware.auth import UserContext, extract_user_context_from_metadata
from ..middleware.error_handler import grpc_error_mapper

logger = structlog.get_logger()


class LoggingInterceptor(grpc.ServerInterceptor):
    """请求日志 + 延迟统计."""

    def intercept_service(
        self,
        continuation: Callable[[grpc.HandlerCallDetails], grpc.RpcMethodHandler],
        handler_call_details: grpc.HandlerCallDetails,
    ) -> grpc.RpcMethodHandler:
        method = handler_call_details.method
        start = time.monotonic()

        def log_wrapper(handler: grpc.RpcMethodHandler) -> grpc.RpcMethodHandler:
            original_behavior = handler.unary_unary

            def new_behavior(request: Any, context: grpc.ServicerContext) -> Any:
                latency_ms = int((time.monotonic() - start) * 1000)
                try:
                    response = original_behavior(request, context)  # type: ignore[misc]
                    logger.info(
                        "grpc_request",
                        method=method,
                        latency_ms=latency_ms,
                        status="ok",
                    )
                    return response
                except Exception as exc:
                    logger.error(
                        "grpc_request_error",
                        method=method,
                        latency_ms=latency_ms,
                        error=str(exc),
                    )
                    raise

            handler.unary_unary = new_behavior  # type: ignore[method-assign]
            return handler

        handler = continuation(handler_call_details)
        if handler is None:
            return None
        return log_wrapper(handler)


class ErrorInterceptor(grpc.ServerInterceptor):
    """异常捕获 → gRPC status code 映射."""

    def intercept_service(
        self,
        continuation: Callable[[grpc.HandlerCallDetails], grpc.RpcMethodHandler],
        handler_call_details: grpc.HandlerCallDetails,
    ) -> grpc.RpcMethodHandler:
        handler = continuation(handler_call_details)
        if handler is None:
            return None

        original_behavior = handler.unary_unary

        def new_behavior(request: Any, context: grpc.ServicerContext) -> Any:
            try:
                return original_behavior(request, context)  # type: ignore[misc]
            except AIError as exc:
                code, msg, grpc_status = grpc_error_mapper(exc)
                logger.warning(
                    "grpc_ai_error",
                    method=handler_call_details.method,
                    error_code=code,
                    message=msg,
                )
                context.abort(_grpc_status(grpc_status), f"{code}: {msg}")
            except Exception as exc:
                code, msg, grpc_status = grpc_error_mapper(exc)
                logger.error(
                    "grpc_unhandled_error",
                    method=handler_call_details.method,
                    error=str(exc),
                )
                context.abort(_grpc_status(grpc_status), f"{code}: {msg}")

        handler.unary_unary = new_behavior  # type: ignore[method-assign]
        return handler


class AuthInterceptor(grpc.ServerInterceptor):
    """从 gRPC metadata 提取用户上下文，存入 context."""

    def intercept_service(
        self,
        continuation: Callable[[grpc.HandlerCallDetails], grpc.RpcMethodHandler],
        handler_call_details: grpc.HandlerCallDetails,
    ) -> grpc.RpcMethodHandler:
        handler = continuation(handler_call_details)
        if handler is None:
            return None

        original_behavior = handler.unary_unary

        def new_behavior(request: Any, context: grpc.ServicerContext) -> Any:
            ctx = extract_user_context_from_metadata(handler_call_details.invocation_metadata)
            # 将 UserContext 存入 context 供 servicer 使用
            context.user_context = ctx  # type: ignore[attr-defined]
            return original_behavior(request, context)  # type: ignore[misc]

        handler.unary_unary = new_behavior  # type: ignore[method-assign]
        return handler


def _grpc_status(code: int) -> grpc.StatusCode:
    """数字 status code → grpc.StatusCode 枚举."""
    status_map = {
        0: grpc.StatusCode.OK,
        1: grpc.StatusCode.CANCELLED,
        2: grpc.StatusCode.UNKNOWN,
        3: grpc.StatusCode.INVALID_ARGUMENT,
        4: grpc.StatusCode.DEADLINE_EXCEEDED,
        5: grpc.StatusCode.NOT_FOUND,
        6: grpc.StatusCode.ALREADY_EXISTS,
        7: grpc.StatusCode.PERMISSION_DENIED,
        8: grpc.StatusCode.UNAUTHENTICATED,
        9: grpc.StatusCode.RESOURCE_EXHAUSTED,
        10: grpc.StatusCode.FAILED_PRECONDITION,
        11: grpc.StatusCode.ABORTED,
        12: grpc.StatusCode.OUT_OF_RANGE,
        13: grpc.StatusCode.INTERNAL,
        14: grpc.StatusCode.UNAVAILABLE,
        15: grpc.StatusCode.DATA_LOSS,
    }
    return status_map.get(code, grpc.StatusCode.UNKNOWN)


def get_user_context(context: grpc.ServicerContext) -> UserContext:
    """从 ServicerContext 提取 UserContext（AuthInterceptor 注入）."""
    return getattr(context, "user_context", UserContext())
