"""
Clerk Authentication Middleware
Verifies Clerk JWT tokens from frontend requests using Clerk Backend API SDK
"""

import base64
import json
import logging

from fastapi import Depends, HTTPException, Request, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

try:
    from clerk_backend_api import Clerk

    CLERK_AVAILABLE = True
except ImportError:
    Clerk = None
    CLERK_AVAILABLE = False

from config import CLERK_SECRET_KEY

logger = logging.getLogger(__name__)

# Security scheme for OpenAPI docs
security = HTTPBearer(auto_error=False)

# Global Clerk client instance
_clerk_client = None


def get_clerk_client():
    """Get or initialize Clerk client"""
    global _clerk_client

    if not CLERK_AVAILABLE:
        logger.warning("⚠️ clerk-backend-api not installed. Install with: pip install clerk-backend-api")
        return None

    if _clerk_client is None and CLERK_SECRET_KEY:
        try:
            if Clerk is None:
                return None
            _clerk_client = Clerk(bearer_auth=CLERK_SECRET_KEY)
            logger.info("✅ Clerk client initialized")
        except Exception as e:
            logger.error(f"❌ Failed to initialize Clerk client: {e}")
            return None

    return _clerk_client


async def verify_clerk_token(request: Request, credentials: HTTPAuthorizationCredentials | None = None) -> dict:
    """
    Verify Clerk JWT token and return user info using Clerk Backend API SDK.

    Args:
        request: FastAPI Request object
        credentials: HTTP Bearer token from Authorization header (optional)

    Returns:
        dict: User information from Clerk

    Raises:
        HTTPException: If token is invalid or missing
    """
    clerk = get_clerk_client()
    if not clerk:
        # In development, if Clerk is not configured, allow requests
        # In production, you should raise an error
        logger.warning("⚠️ Clerk not configured, allowing request (dev mode)")
        return {"user_id": "dev_user", "email": "dev@example.com", "clerk_user_id": "dev_user"}

    try:
        # Convert FastAPI Request to httpx.Request for Clerk SDK
        import httpx

        # Create httpx.Request from FastAPI Request
        url = str(request.url)
        method = request.method
        headers = dict(request.headers)
        # Get body if exists
        body = await request.body() if hasattr(request, "body") else b""

        httpx_request = httpx.Request(method=method, url=url, headers=headers, content=body)

        # Authenticate request using Clerk SDK
        # Try with options first, fallback to without options
        try:
            # Debug: Log token presence
            auth_header = request.headers.get("Authorization", "")
            has_token = bool(auth_header and auth_header.startswith("Bearer "))
            token_preview = ""
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.replace("Bearer ", "")
                token_preview = token[:20] + "..." if len(token) > 20 else token[:10] + "..."
            logger.info(f"🔍 Clerk auth: has_token={has_token}, path={request.url.path}, token_preview={token_preview}")

            # Try to import AuthenticateRequestOptions if available
            AuthenticateRequestOptions = None
            try:
                from clerk_backend_api.jwks_helpers import AuthenticateRequestOptions  # type: ignore
            except ImportError:
                try:
                    from clerk_backend_api.security.types import AuthenticateRequestOptions  # type: ignore
                except ImportError:
                    pass

            if AuthenticateRequestOptions:
                request_state = clerk.authenticate_request(httpx_request, AuthenticateRequestOptions())
            else:
                # Fallback: authenticate without options (if SDK supports it)
                # Some SDK versions require options, so we'll handle the error
                try:
                    # Try with empty dict as options
                    request_state = clerk.authenticate_request(httpx_request, {})  # type: ignore
                except (TypeError, ValueError):
                    # If that fails, try without any options
                    request_state = clerk.authenticate_request(httpx_request)  # type: ignore
        except Exception as auth_error:
            logger.error(f"❌ Clerk authenticate_request failed: {auth_error}")
            logger.error(f"   Path: {request.url.path}, Method: {request.method}")
            logger.error(f"   Has Auth Header: {bool(request.headers.get('Authorization'))}")
            auth_header = request.headers.get("Authorization", "")
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.replace("Bearer ", "")
                logger.error(f"   Token preview: {token[:30]}... (length: {len(token)})")
            logger.error(f"   Error type: {type(auth_error).__name__}")
            logger.error(f"   Error details: {auth_error!s}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, detail=f"Authentication failed: {auth_error!s}"
            )

        if not request_state.is_signed_in:
            logger.warning(f"⚠️ Clerk auth: request_state.is_signed_in=False for {request.url.path}")
            auth_header = request.headers.get("Authorization", "")
            logger.warning(f"   Auth header present: {bool(auth_header)}")
            if auth_header and auth_header.startswith("Bearer "):
                token = auth_header.replace("Bearer ", "")
                logger.warning(f"   Token preview: {token[:30]}... (length: {len(token)})")
                logger.warning(f"   Token starts with 'eyJ': {token.startswith('eyJ')}")
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, detail="Not signed in. Please check your Clerk token."
            )

        # Extract user info from request state
        # Try different ways to get user_id based on SDK version
        user_id = None
        try:
            # Try to get from auth object
            auth = request_state.to_auth() if hasattr(request_state, "to_auth") else None
            if auth:
                user_id = getattr(auth, "user_id", None) or getattr(auth, "userId", None)
        except Exception:
            pass

        # If still no user_id, try direct access
        if not user_id:
            user_id = getattr(request_state, "user_id", None) or getattr(request_state, "userId", None)

        if not user_id:
            # Fallback: try decode JWT sub from Authorization header (best effort, no verify)
            token = None
            if credentials and credentials.credentials:
                token = credentials.credentials
            elif request.headers.get("Authorization", "").startswith("Bearer "):
                token = request.headers.get("Authorization", "")[7:]

            if token:
                try:
                    parts = token.split(".")
                    if len(parts) >= 2:
                        payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4)
                        payload = json.loads(base64.urlsafe_b64decode(payload_b64).decode("utf-8"))
                        user_id = payload.get("sub") or payload.get("userId") or payload.get("uid")
                except Exception as decode_err:
                    logger.warning(f"⚠️ Failed to decode Clerk JWT payload for user_id: {decode_err}")

            if not user_id:
                logger.warning("⚠️ Could not extract user_id from Clerk request_state or JWT; using 'unknown'")
                user_id = "unknown"

        return {
            "user_id": user_id,
            "clerk_user_id": user_id,
            "email": None,  # Will be fetched if needed via API
            "first_name": None,
            "last_name": None,
        }
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"❌ Clerk token verification failed: {e}")
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired token")


async def get_current_user(
    request: Request, credentials: HTTPAuthorizationCredentials | None = Depends(security)
) -> dict:
    """
    Get current authenticated user from Clerk token.
    Can be used as FastAPI dependency: user = Depends(get_current_user)

    Also checks X-User-Id header as fallback for development.
    """
    # Try to verify Clerk token first
    try:
        return await verify_clerk_token(request, credentials)
    except HTTPException:
        pass

    # Fallback: Check X-User-Id header (for development/testing)
    user_id_header = request.headers.get("X-User-Id")
    if user_id_header:
        logger.warning(f"⚠️ Using X-User-Id header (dev mode): {user_id_header}")
        return {
            "user_id": user_id_header,
            "email": f"{user_id_header}@example.com",
            "clerk_user_id": user_id_header,
        }

    # No auth found
    raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")


async def get_optional_user(
    request: Request, credentials: HTTPAuthorizationCredentials | None = Depends(security)
) -> dict | None:
    """
    Get current user if authenticated, otherwise return None.
    Useful for endpoints that work with or without auth.
    """
    try:
        return await get_current_user(request, credentials)
    except HTTPException:
        return None


def get_user_from_request(request: Request) -> dict:
    """
    Helper function để lấy user info từ request.state (đã được middleware verify).

    Middleware đã verify token và attach vào request.state.user,
    nên routes chỉ cần gọi hàm này để lấy user info.

    Args:
        request: FastAPI Request object

    Returns:
        dict: User info từ request.state.user

    Raises:
        HTTPException 401: Nếu không có user trong request.state (chưa được authenticate)
    """
    user = getattr(request.state, "user", None)
    if not user:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required")
    return user


def get_user_id_from_request(request: Request) -> str:
    """
    Helper function để lấy user_id từ request.state (đã được middleware verify).

    Args:
        request: FastAPI Request object

    Returns:
        str: User ID (Clerk ID) từ request.state.user_id

    Raises:
        HTTPException 401: Nếu không có user_id trong request.state
    """
    user_id = getattr(request.state, "user_id", None)
    if not user_id:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="User ID not found. Authentication required."
        )
    return user_id


def get_clerk_token_from_request(request: Request) -> str | None:
    """
    Helper function để lấy Clerk token từ request.state (để forward cho Supabase RLS).

    Args:
        request: FastAPI Request object

    Returns:
        Optional[str]: Clerk token nếu có, None nếu không có
    """
    return getattr(request.state, "clerk_token", None)
