"""Authentication decorators for tool permissions.

This module provides decorators to control tool accessibility based on
Auth0 roles and permissions.

Usage:
    from point_topic_mcp.auth import requires_permissions, public

    @public
    @mcp.tool()
    async def get_server_info() -> dict:
        return {"status": "ok"}  # Available to everyone

    @requires_permissions(["read:data"])
    @mcp.tool()
    async def query_data() -> dict:
        return {"data": "sensitive"}  # Requires permission
"""

from typing import Union
from functools import wraps

# Registry to track public tools by function name
PUBLIC_TOOLS: set[str] = set()

# Registry to track tools with permission requirements
# Format: {tool_name: [permissions_list]}
TOOL_PERMISSIONS: dict[str, list[str]] = {}


def public(func):
    """Mark tool as available to everyone (no authentication required).

    Tools with this decorator will work regardless of authentication status.

    Args:
        func: The tool function to mark as public

    Returns:
        The original function (registered as public)
    """
    PUBLIC_TOOLS.add(func.__name__)
    return func


def is_public_tool(name: str) -> bool:
    """Check if a tool is marked as public.

    Args:
        name: The tool function name to check

    Returns:
        True if the tool is public, False otherwise
    """
    return name in PUBLIC_TOOLS


def requires_permissions(permissions: Union[str, list[str]]):
    """Require specific permissions to use a tool.

    This decorator marks tools that require specific Auth0 permissions.
    The permission check happens at tool call time via the middleware.

    Args:
        permissions: Single permission string or list of permission strings.
                    User must have ALL listed permissions to access the tool.
                    Examples: "read:data" or ["read:data", "write:data"]

    Returns:
        Decorator function that marks the tool with permission requirements

    Example:
        @requires_permissions("read:data")
        @mcp.tool()
        async def get_data():
            return {"data": "secret"}

        @requires_permissions(["admin:users", "write:config"])
        @mcp.tool()
        async def admin_action():
            return {"action": "performed"}
    """
    # Normalize permissions to list
    if isinstance(permissions, str):
        required_perms = [permissions]
    else:
        required_perms = list(permissions)

    def decorator(func):
        """Store permission requirements for this tool."""
        TOOL_PERMISSIONS[func.__name__] = required_perms
        return func

    return decorator


def get_tool_permissions(tool_name: str) -> list[str]:
    """Get required permissions for a tool.

    Args:
        tool_name: Name of the tool to check

    Returns:
        List of required permissions, empty list if no requirements
    """
    return TOOL_PERMISSIONS.get(tool_name, [])


def has_tool_permissions(tool_name: str, user_permissions: list[str]) -> bool:
    """Check if user has required permissions for a tool.

    Args:
        tool_name: Name of the tool to check access for
        user_permissions: List of permissions the user has

    Returns:
        True if user has all required permissions or is ptAdmin
    """
    # Public tools require no permissions
    if is_public_tool(tool_name):
        return True

    # Get required permissions for this tool
    required = get_tool_permissions(tool_name)

    # No requirements means accessible to all authenticated users
    if not required:
        return True

    # ptAdmin bypasses all permission checks
    admin_roles = ["ptAdmin"]
    if any(role in user_permissions for role in admin_roles):
        return True

    # Check if user has ALL required permissions
    return all(perm in user_permissions for perm in required)


__all__ = [
    "public",
    "is_public_tool",
    "PUBLIC_TOOLS",
    "requires_permissions",
    "get_tool_permissions",
    "has_tool_permissions",
    "TOOL_PERMISSIONS",
]
