"""
Authentication middleware for protecting endpoints
"""
from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse
from typing import Optional, Callable
import structlog

from services.auth_service import AuthService
from models import AsyncSessionLocal
from models.user import User, UserRole

logger = structlog.get_logger()


async def get_user_from_token(request: Request) -> Optional[User]:
    """
    Extract and validate JWT token from request, return user if valid

    Args:
        request: FastAPI request object

    Returns:
        User object if authenticated, None otherwise
    """
    # Extract token from Authorization header
    auth_header = request.headers.get("Authorization")
    if not auth_header:
        return None

    # Check if it's a Bearer token
    parts = auth_header.split()
    if len(parts) != 2 or parts[0].lower() != "bearer":
        return None

    token = parts[1]

    # Validate token and get user
    async with AsyncSessionLocal() as db:
        auth_service = AuthService(db)
        user = await auth_service.validate_token(token)
        return user


async def require_auth(request: Request, call_next: Callable):
    """
    Middleware to require authentication for protected routes

    This middleware should be applied to specific routes via dependencies,
    not globally, to allow public endpoints like /health and /docs
    """
    user = await get_user_from_token(request)

    if not user:
        logger.warning("authentication_required",
                      path=request.url.path,
                      method=request.method,
                      ip=request.client.host if request.client else None)

        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "error": "Unauthorized",
                "message": "Authentication required"
            },
            headers={"WWW-Authenticate": "Bearer"}
        )

    # Add user to request state for downstream handlers
    request.state.user = user
    request.state.user_id = user.id

    logger.info("authenticated_request",
               path=request.url.path,
               method=request.method,
               user_id=str(user.id),
               username=user.username,
               role=user.role.value)

    response = await call_next(request)
    return response


def require_role(required_role: UserRole):
    """
    Dependency factory to require specific role

    Usage:
        @app.get("/admin-only", dependencies=[Depends(require_role(UserRole.ADMINISTRATOR))])

    Args:
        required_role: Minimum required role

    Returns:
        Dependency function
    """
    async def role_checker(request: Request) -> User:
        user = await get_user_from_token(request)

        if not user:
            logger.warning("authentication_required_role_check",
                          path=request.url.path,
                          required_role=required_role.value)
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Authentication required",
                headers={"WWW-Authenticate": "Bearer"}
            )

        if not user.has_permission(required_role):
            logger.warning("permission_denied",
                          path=request.url.path,
                          user_id=str(user.id),
                          user_role=user.role.value,
                          required_role=required_role.value)
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Requires {required_role.value} role or higher"
            )

        # Add user to request state
        request.state.user = user
        request.state.user_id = user.id

        return user

    return role_checker


# Convenience dependencies for common role checks
async def require_viewer(request: Request) -> User:
    """Require at least viewer role (allows all authenticated users)"""
    return await require_role(UserRole.VIEWER)(request)


async def require_operator(request: Request) -> User:
    """Require at least operator role"""
    return await require_role(UserRole.OPERATOR)(request)


async def require_administrator(request: Request) -> User:
    """Require administrator role"""
    return await require_role(UserRole.ADMINISTRATOR)(request)


def get_current_user(request: Request) -> Optional[User]:
    """
    Get currently authenticated user from request state

    This should be used after authentication middleware has run

    Args:
        request: FastAPI request object

    Returns:
        User object if authenticated, None otherwise
    """
    return getattr(request.state, "user", None)


def get_client_ip(request: Request) -> Optional[str]:
    """
    Extract client IP address from request

    Checks X-Forwarded-For header first (if behind proxy),
    then falls back to direct client IP

    Args:
        request: FastAPI request object

    Returns:
        Client IP address string or None
    """
    # Check X-Forwarded-For header (if behind proxy/load balancer)
    forwarded_for = request.headers.get("X-Forwarded-For")
    if forwarded_for:
        # X-Forwarded-For can contain multiple IPs, take the first
        return forwarded_for.split(",")[0].strip()

    # Fall back to direct client IP
    if request.client:
        return request.client.host

    return None


def get_user_agent(request: Request) -> Optional[str]:
    """
    Extract user agent from request headers

    Args:
        request: FastAPI request object

    Returns:
        User agent string or None
    """
    return request.headers.get("User-Agent")
