"""Flask route decorator for PyAuthz authorization.

The @requires() decorator checks authorization before the route handler
executes. If denied, raises Denied which is caught by the extension's
error handler and returns a 403 JSON response.
"""

from __future__ import annotations

import functools
from collections.abc import Callable
from typing import Any

from flask import g

from pyauthz import PyAuthz, Resource, Subject

# Type for optional resource resolver
ResourceResolver = Callable[..., Resource]


def requires(
    action: str,
    *,
    resource_resolver: ResourceResolver | None = None,
    resource_type: str | None = None,
) -> Callable:
    """Decorator that enforces authorization on a Flask route.

    Args:
        action: The action to authorize, e.g. "documents:read" or "documents:delete".
        resource_resolver: Optional callable that produces a Resource from the request
            and route kwargs. Signature: (request, **kwargs) -> Resource.
        resource_type: Optional resource type. If not provided, inferred from action.

    Example:
        @app.route("/documents/<doc_id>", methods=["DELETE"])
        @requires("documents:delete")
        def delete_document(doc_id):
            ...

        @app.route("/documents/<doc_id>", methods=["PUT"])
        @requires("documents:write", resource_resolver=resolve_document)
        def update_document(doc_id):
            ...
    """

    def decorator(fn: Callable) -> Callable:
        @functools.wraps(fn)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            from flask import request

            rp: PyAuthz = g.pyauthz
            subject: Subject = g.pyauthz_subject

            # Resolve the resource
            if resource_resolver is not None:
                resource = resource_resolver(request, **kwargs)
            else:
                # Build a default resource from the action
                if resource_type:
                    rtype = resource_type
                elif ":" in action:
                    rtype = action.split(":")[0]
                else:
                    rtype = "unknown"
                resource = Resource(id="", type=rtype)

            # Authorize — raises Denied on failure
            decision = rp.authorize(subject, action, resource)
            g.pyauthz_decision = decision

            return fn(*args, **kwargs)

        return wrapper

    return decorator
