"""
RBAC permission-enforcement dependency for FastAPI — PRD-008.

Usage:
    @router.post("/refund", dependencies=[Depends(require_permission("transactions:refund"))])
    async def refund(...):
        ...
"""

from __future__ import annotations

import json
import logging

from fastapi import Depends, HTTPException, status
from sqlalchemy.orm import Session

from src.core.database import get_db
from src.apps.auth.utils.auth import get_current_active_user, get_current_merchant
from src.apps.role_permissions import crud

logger = logging.getLogger(__name__)

PERM_CACHE_TTL = 60  # seconds


def require_permission(slug: str):
    """
    FastAPI dependency factory.
    Checks Redis cache first (key: perms:{user_id}:{merchant_id}), falls back to DB.

    Superusers bypass all permission checks.
    Owners (role_rank == 3) bypass permission checks.
    """

    async def _check(
        current_user=Depends(get_current_active_user),
        merchant=Depends(get_current_merchant),
        db: Session = Depends(get_db),
    ):
        # Superuser bypass
        if getattr(current_user, "is_superuser", False):
            return current_user

        user_role = crud.get_user_role(
            db, user_id=current_user.id, merchant_id=merchant.id
        )
        if user_role is None:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="No role assigned for this merchant.",
            )

        # Owner bypass
        if user_role.role and user_role.role.role_rank == 3:
            return current_user

        # Try Redis cache
        permissions: set[str] = set()
        cache_hit = False
        try:
            import redis as redis_lib
            from src.core.config import settings

            url = getattr(settings, "CELERY_RESULT_BACKEND", None) or getattr(
                settings, "REDIS_URL", None
            )
            if url and "redis" in url:
                r = redis_lib.from_url(url, decode_responses=True)
                cache_key = f"perms:{current_user.id}:{merchant.id}"
                cached = r.get(cache_key)
                if cached:
                    permissions = set(json.loads(cached))
                    cache_hit = True
                else:
                    permissions = crud.get_user_permissions_from_db(
                        db,
                        user_id=current_user.id,
                        merchant_id=merchant.id,
                    )
                    r.setex(
                        cache_key,
                        PERM_CACHE_TTL,
                        json.dumps(list(permissions)),
                    )
            else:
                permissions = crud.get_user_permissions_from_db(
                    db, user_id=current_user.id, merchant_id=merchant.id
                )
        except Exception:
            permissions = crud.get_user_permissions_from_db(
                db, user_id=current_user.id, merchant_id=merchant.id
            )

        if slug not in permissions:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Permission denied: '{slug}' required.",
            )

        return current_user

    return _check


def invalidate_permission_cache(user_id: int, merchant_id: int) -> None:
    """Synchronously invalidate the Redis permission cache for a user."""
    try:
        import redis as redis_lib
        from src.core.config import settings

        url = getattr(settings, "CELERY_RESULT_BACKEND", None) or getattr(
            settings, "REDIS_URL", None
        )
        if url and "redis" in url:
            r = redis_lib.from_url(url, decode_responses=True)
            r.delete(f"perms:{user_id}:{merchant_id}")
    except Exception:
        pass
