from collections.abc import Callable

from fastapi import Depends, HTTPException, status
from common_logging import get_logger

from app.api.deps import get_current_user
from app.core.permissions import check_permission as casbin_check_permission
from app.models.user import User

logger = get_logger(__name__)


def require_permission(resource: str, action: str) -> Callable:

    async def permission_checker(current_user: User = Depends(get_current_user)) -> User:
        tenant_id = current_user.tenant_id
        if not tenant_id:
            from app.core.permissions import get_user_roles

            user_roles = get_user_roles(current_user.id, 0)
            role_codes = [role["role_code"] for role in user_roles]
            if not any(code in ["platform_admin", "platform_user"] for code in role_codes):
                logger.warning(f"Non-platform user without tenant_id: user={current_user.id}")
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="Non-platform users must have a tenant_id",
                )
            tenant_id = 0
        has_permission = casbin_check_permission(
            user_id=current_user.id, tenant_id=tenant_id, resource=resource, action=action
        )
        if not has_permission:
            logger.warning(f"Permission denied: user={current_user.id}, resource={resource}, action={action}")
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Permission denied: {action} on {resource}",
            )
        return current_user

    return permission_checker


def require_read(resource: str) -> Callable:
    return require_permission(resource, "read")


def require_create(resource: str) -> Callable:
    return require_permission(resource, "create")


def require_update(resource: str) -> Callable:
    return require_permission(resource, "update")


def require_delete(resource: str) -> Callable:
    return require_permission(resource, "delete")


def require_execute(resource: str) -> Callable:
    return require_permission(resource, "execute")


def require_write(resource: str) -> Callable:
    return require_permission(resource, "update")


def require_admin() -> Callable:

    async def admin_checker(current_user: User = Depends(get_current_user)) -> User:
        from app.core.permissions import get_user_roles

        tenant_id = current_user.tenant_id or 0
        user_roles = get_user_roles(current_user.id, tenant_id)
        role_codes = [role["role_code"] for role in user_roles]
        if not any(code in ["platform_admin", "customer_admin"] for code in role_codes):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required"
            )
        return current_user

    return admin_checker


def require_platform_admin() -> Callable:

    async def platform_admin_checker(current_user: User = Depends(get_current_user)) -> User:
        from app.core.permissions import get_user_roles

        tenant_id = current_user.tenant_id or 0
        user_roles = get_user_roles(current_user.id, tenant_id)
        role_codes = [role["role_code"] for role in user_roles]
        if "platform_admin" not in role_codes:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN, detail="Platform admin access required"
            )
        return current_user

    return platform_admin_checker


def require_tenant_admin() -> Callable:

    async def tenant_admin_checker(current_user: User = Depends(get_current_user)) -> User:
        from app.core.permissions import get_user_roles

        tenant_id = current_user.tenant_id or 0
        user_roles = get_user_roles(current_user.id, tenant_id)
        role_codes = [role["role_code"] for role in user_roles]
        if "customer_admin" not in role_codes:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN, detail="Tenant admin access required"
            )
        return current_user

    return tenant_admin_checker
