"""
Middleware Module - Gom Auth + Rate Limit
Singleton Pattern cho cả 2 services
"""

from __future__ import annotations

import asyncio
import json
import logging
from collections.abc import Callable
from typing import TYPE_CHECKING

from fastapi import Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

from common.canifa_api import extract_user_id_from_canifa_response, verify_canifa_token
from common.message_limit import message_limit_service
from common.rate_limit import rate_limit_service

if TYPE_CHECKING:
    from fastapi import FastAPI

logger = logging.getLogger(__name__)


# =============================================================================
# CONFIGURATION
# =============================================================================

# Public endpoints - không cần auth
PUBLIC_PATHS = {
    "/",
    "/health",
    "/docs",
    "/openapi.json",
    "/redoc",
}

# Public path prefixes
PUBLIC_PATH_PREFIXES = [
    "/static",
    "/mock",
    "/api/mock",
]


# =============================================================================
# AUTH + RATE LIMIT MIDDLEWARE CLASS
# =============================================================================

# Paths that need rate limit check
RATE_LIMITED_PATHS = [
    "/api/agent/chat",
    "/api/agent/chat-dev",
]


class CanifaAuthMiddleware(BaseHTTPMiddleware):
    """
    Canifa Authentication + Rate Limit Middleware

    Flow:
    1. Frontend gửi request với Authorization: Bearer <canifa_token>
    2. Middleware verify token với Canifa API → extract customer_id
    3. Check message rate limit (Guest: 10, User: 100)
    4. Attach user info vào request.state
    5. Routes lấy trực tiếp từ request.state
    """

    @staticmethod
    def _make_receive(body: bytes):
        """Return an async receive callable that replays the cached body ONCE,
        then blocks forever on subsequent calls. This prevents
        StreamingResponse's listen_for_disconnect from getting an unexpected
        http.request message (it expects http.disconnect)."""
        _sent = False

        async def receive():
            nonlocal _sent
            if not _sent:
                _sent = True
                return {"type": "http.request", "body": body}
            # Block forever — the task will be cancelled when the
            # response stream finishes or the client disconnects.
            await asyncio.Event().wait()
        return receive

    async def dispatch(self, request: Request, call_next: Callable):
        path = request.url.path
        method = request.method

        # ✅ Allow OPTIONS requests (CORS preflight)
        if method == "OPTIONS":
            return await call_next(request)

        # Skip public endpoints
        if path in PUBLIC_PATHS or any(path.startswith(prefix) for prefix in PUBLIC_PATH_PREFIXES):
            return await call_next(request)

        # =====================================================================
        # STEP 1: AUTHENTICATION & IDENTITY
        # =====================================================================
        try:
            auth_header = request.headers.get("Authorization")
            request.state.is_authenticated = False
            request.state.user = None
            request.state.user_id = None
            request.state.device_id = None

            # 1. Thử xác thực qua Header trước (Bearer Token)
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.replace("Bearer ", "")

                try:
                    user_data = await verify_canifa_token(token)
                    user_id = await extract_user_id_from_canifa_response(user_data)
                    if user_id:
                        request.state.user = user_data
                        request.state.user_id = user_id
                        request.state.is_authenticated = True
                        logger.debug(f"✅ Auth Success: User {user_id}")
                except Exception as e:
                    logger.error(f"❌ Auth Error: {e}")

            # 2. Lấy Device ID (Để tracking hoặc dùng cho Guest)
            # ⚠️ Skip body reading for SSE/streaming endpoints — reading the
            # body inside BaseHTTPMiddleware corrupts the receive chain and
            # causes "Unexpected message received: http.request" errors.
            SSE_PATHS = {"/api/sql-dashboard", "/api/sql-chat/stream", "/api/report-agent"}
            device_id = ""
            if method in ["POST", "PUT", "PATCH"] and path not in SSE_PATHS:
                try:
                    body_bytes = await request.body()
                    request._receive = self._make_receive(body_bytes)
                    if body_bytes:
                        try:
                            data = json.loads(body_bytes)
                            device_id = data.get("device_id", "")
                        except:
                            pass
                except:
                    pass

            # Nếu không có trong body, lấy từ Header hoặc IP
            if not device_id:
                device_id = request.headers.get("device_id") or (
                    f"unknown_{request.client.host}" if request.client else "unknown"
                )

            request.state.device_id = device_id

        except Exception as e:
            logger.error(f"❌ Middleware Error: {e}")

        # =====================================================================
        # STEP 2: RATE LIMIT CHECK
        # =====================================================================
        if path in RATE_LIMITED_PATHS:
            try:
                # Identify User for Rate Limit: Ưu tiên user_id (nếu đã login), không thì dùng device_id
                identity_key = request.state.user_id if request.state.is_authenticated else request.state.device_id

                if identity_key:
                    can_send, limit_info = await message_limit_service.check_limit(
                        identity_key=identity_key,
                        is_authenticated=request.state.is_authenticated,
                    )
                    request.state.limit_info = limit_info

                    if not can_send:
                        logger.warning(f"⚠️ Rate Limit: {identity_key} | {limit_info['used']}/{limit_info['limit']}")
                        return JSONResponse(
                            status_code=429,
                            content={
                                "status": "error",
                                "error_code": "MESSAGE_LIMIT_EXCEEDED",
                                "message": limit_info["message"],
                                "require_login": limit_info["require_login"],
                                "limit_info": {
                                    "limit": limit_info["limit"],
                                    "used": limit_info["used"],
                                    "remaining": limit_info["remaining"],
                                    "reset_seconds": limit_info["reset_seconds"],
                                },
                            },
                        )
            except Exception as e:
                logger.error(f"❌ Rate Limit Check Error: {e}")

        return await call_next(request)


# =============================================================================
# MIDDLEWARE MANAGER - Singleton to manage all middlewares
# =============================================================================
class MiddlewareManager:
    """
    Middleware Manager - Singleton Pattern
    Quản lý và setup tất cả middlewares cho FastAPI app

    Usage:
        from common.middleware import middleware_manager

        app = FastAPI()
        middleware_manager.setup(app, enable_auth=True, enable_rate_limit=True)
    """

    _instance: MiddlewareManager | None = None
    _initialized: bool = False

    def __new__(cls) -> MiddlewareManager:
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self) -> None:
        if MiddlewareManager._initialized:
            return

        self._auth_enabled = False
        self._rate_limit_enabled = False

        MiddlewareManager._initialized = True
        logger.info("✅ MiddlewareManager initialized")

    def setup(
        self,
        app: FastAPI,
        *,
        enable_auth: bool = True,
        enable_rate_limit: bool = True,
        enable_cors: bool = True,
        cors_origins: list[str] | None = None,
    ) -> None:
        """
        Setup tất cả middlewares cho FastAPI app.

        Args:
            app: FastAPI application
            enable_auth: Bật Canifa authentication middleware
            enable_rate_limit: Bật rate limiting
            enable_cors: Bật CORS middleware
            cors_origins: List origins cho CORS (default: ["*"])

        Note:
            Thứ tự middleware quan trọng! Middleware thêm sau sẽ chạy TRƯỚC.
            Order: CORS → Auth → RateLimit → SlowAPI
        """
        # 1. CORS Middleware (thêm cuối cùng để chạy đầu tiên)
        if enable_cors:
            self._setup_cors(app, cors_origins or ["*"])

        # 2. Auth Middleware
        if enable_auth:
            self._setup_auth(app)

        # 3. Rate Limit Middleware
        if enable_rate_limit:
            self._setup_rate_limit(app)

        logger.info(f"✅ Middlewares configured: CORS={enable_cors}, Auth={enable_auth}, RateLimit={enable_rate_limit}")

    def _setup_cors(self, app: FastAPI, origins: list[str]) -> None:
        """Setup CORS middleware."""
        app.add_middleware(
            CORSMiddleware,
            allow_origins=origins,
            allow_credentials=True,
            allow_methods=["*"],
            allow_headers=["*"],
        )
        logger.info(f"✅ CORS middleware enabled (origins: {origins})")

    def _setup_auth(self, app: FastAPI) -> None:
        """Setup Canifa auth middleware."""
        app.add_middleware(CanifaAuthMiddleware)
        self._auth_enabled = True
        logger.info("✅ Canifa Auth middleware enabled")

    def _setup_rate_limit(self, app: FastAPI) -> None:
        """Setup rate limiting."""

        rate_limit_service.setup(app)
        self._rate_limit_enabled = True
        logger.info("✅ Rate Limit middleware enabled")

    @property
    def is_auth_enabled(self) -> bool:
        return self._auth_enabled

    @property
    def is_rate_limit_enabled(self) -> bool:
        return self._rate_limit_enabled


middleware_manager = MiddlewareManager()
