"""Permission helpers for Pydantic AI tool authorization.

This module provides permission checking utilities for MCP and function tools.
Supports scoped API key permissions for fine-grained tool access control.
"""

from collections.abc import Callable
from typing import Any

from django.core.exceptions import PermissionDenied


def tool_requires_scope(scope: str) -> Callable:
    """Factory to create scope-checking permission handlers.

    Creates a process_tool_call callback that verifies the API key has
    the required scope before allowing tool execution.

    Args:
        scope: The tool scope required (e.g., "admin_db", "email").

    Returns:
        A callback function suitable for MCPServerStdio.process_tool_call.

    Example:
        >>> from lightwave.ai.permissions import tool_requires_scope
        >>> from pydantic_ai.mcp import MCPServerStdio
        >>>
        >>> server = MCPServerStdio(
        ...     command="uvx",
        ...     args=["mcp-postgres"],
        ...     process_tool_call=tool_requires_scope("admin_db"),
        ... )
    """

    def check_scope(ctx, direct_call_tool, name: str, tool_args: dict[str, Any]):
        """Verify API key has required scope before executing tool.

        Args:
            ctx: The run context containing user dependencies.
            direct_call_tool: The tool function to call if authorized.
            name: Name of the tool being called.
            tool_args: Arguments for the tool call.

        Returns:
            The result of calling the tool if authorized.

        Raises:
            PermissionDenied: If no API key present or key lacks required scope.
        """
        # Get API key from the context dependencies
        api_key = getattr(ctx.deps, "api_key", None) if hasattr(ctx, "deps") else None

        if api_key is None:
            raise PermissionDenied(
                f"API key required for '{scope}' operations. "
                "Authenticate with X-API-Key header using a key with the required scope."
            )

        if not api_key.has_tool_scope(scope):
            current_scopes = api_key.get_tool_scopes()
            raise PermissionDenied(
                f"API key '{api_key.name}' lacks scope '{scope}'. "
                f"Current scopes: {current_scopes or '(none)'}. "
                f"Update the key's permissions.tool_scopes to include '{scope}'."
            )

        return direct_call_tool(name, tool_args)

    return check_scope


# Backwards compatibility - deprecated, use tool_requires_scope instead
def tool_requires_superuser(ctx, direct_call_tool, name, tool_args):
    """DEPRECATED: Use tool_requires_scope("admin_db") instead.

    Check if the current user has admin access before executing a tool.
    This function is maintained for backwards compatibility only.

    Args:
        ctx: The run context containing user dependencies.
        direct_call_tool: The tool function to call if authorized.
        name: Name of the tool being called.
        tool_args: Arguments for the tool call.

    Returns:
        The result of calling the tool if authorized.

    Raises:
        PermissionDenied: If user is not a superuser with staff status,
            or if no API key with admin_db scope is present.
    """
    import warnings

    warnings.warn(
        "tool_requires_superuser is deprecated. Use tool_requires_scope('admin_db') instead.",
        DeprecationWarning,
        stacklevel=2,
    )

    # First try the new scope-based check
    api_key = getattr(ctx.deps, "api_key", None) if hasattr(ctx, "deps") else None

    if api_key is not None and api_key.has_tool_scope("admin_db"):
        return direct_call_tool(name, tool_args)

    # Fallback to legacy superuser check
    user = ctx.deps.user if hasattr(ctx, "deps") and hasattr(ctx.deps, "user") else None

    if not user or not (user.is_superuser and user.is_staff):
        raise PermissionDenied(
            "Admin access required for database operations. "
            "Either use an API key with 'admin_db' scope, or authenticate as a superuser."
        )

    return direct_call_tool(name, tool_args)
