"""
RBAC FastAPI Dependencies
==========================
Drop-in FastAPI dependency injectors for protecting routes.

Usage:
    from src.core.rbac.rbac_dependency import require_permission, get_current_role
    from src.core.rbac.permissions_registry import Perm

    @router.get("/applications")
    def list_apps(role=Depends(require_permission(Perm.APPLICATIONS_VIEW))):
        ...

    @router.delete("/applications/{id}")
    def delete_app(id: int, role=Depends(require_permission(Perm.APPLICATIONS_DELETE))):
        ...
"""
from fastapi import Depends, HTTPException, status, Request
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from src.config.settings import settings
from src.core.rbac.rbac_engine import has_permission, has_any_permission, get_role_permissions

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")


def _decode_token(token: str) -> dict:
    """Decode JWT and return its payload."""
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        return payload
    except JWTError as e:
        print(f"JWT_AUTH_DEBUG: Token decode failed: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=f"Invalid or expired token: {str(e)}",
            headers={"WWW-Authenticate": "Bearer"},
        )


def get_current_user_payload(token: str = Depends(oauth2_scheme)) -> dict:
    """Dependency: Decode the JWT and return the full payload dict."""
    return _decode_token(token)


def get_current_role(payload: dict = Depends(get_current_user_payload)) -> str:
    """
    Dependency: Extract the `role` slug from the JWT payload.
    The JWT must contain a 'role' claim e.g. {"sub": "user@x.com", "role": "credit_ops"}.
    """
    role = payload.get("role")
    if not role:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="No role assigned to this user",
        )
    return role


def require_permission(permission: str):
    """
    Factory: Returns a FastAPI dependency that enforces a single permission.
    
    Example:
        @router.get("/apps", dependencies=[Depends(require_permission(Perm.APPLICATIONS_VIEW))])
    """
    def _check(role: str = Depends(get_current_role)):
        if not has_permission(role, permission):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Permission denied: '{permission}' required",
            )
        return role
    return _check


def require_any_permission(*permissions: str):
    """
    Factory: Passes if the user has AT LEAST ONE of the listed permissions.
    
    Example:
        @router.get("/report", dependencies=[Depends(require_any_permission(Perm.ANALYTICS_VIEW, Perm.REPORTS_EXPORT))])
    """
    def _check(role: str = Depends(get_current_role)):
        if not has_any_permission(role, list(permissions)):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Permission denied: one of {list(permissions)} required",
            )
        return role
    return _check


def get_role_permission_set(role: str = Depends(get_current_role)) -> set:
    """
    Dependency: Returns the full permission set for the current user's role.
    Useful when you want to conditionally mask fields inside a route handler.
    
    Example:
        @router.get("/application/{id}")
        def get_app(id: int, perms: set = Depends(get_role_permission_set)):
            app = fetch_app(id)
            if Perm.PII_PHONE not in perms:
                app["phone"] = "***"
            return app
    """
    return get_role_permissions(role)
