"""Primitif Approval — human-in-the-loop gate for AI agents.

Usage::

    from primitif import approval

    req = approval.create_request(
        "Send contract",
        context={"client": "Acme", "amount": 50000},
    )
    print(req.approval_url)  # share with the human

    status = approval.get_request(req.id)

Decorator usage::

    from primitif.approval import require_approval

    @require_approval
    def send_invoice(client, amount):
        billing.send(client, amount)

    req = send_invoice("Acme", 45000)  # returns ApprovalRequest
"""

from __future__ import annotations

import functools
import inspect
from typing import TYPE_CHECKING, Any, Callable, Literal

from primitif.approval.client import ApprovalAdmin
from primitif.approval.exceptions import (
    ApprovalAuthError,
    ApprovalNetworkError,
    ApprovalError,
    ApprovalNotFoundError,
    ApprovalRateLimitError,
    ApprovalServerError,
    ApprovalValidationError,
)
from primitif.approval.models import ApprovalRequest

if TYPE_CHECKING:
    from primitif._models import PaginatedList


# ---------------------------------------------------------------------------
# Lazy admin client — created on first use from PRIMITIF_API_KEY env var
# ---------------------------------------------------------------------------

_admin: ApprovalAdmin | None = None


def _get_admin() -> ApprovalAdmin:
    global _admin
    if _admin is None:
        from primitif.client import Primitif
        _admin = Primitif().approval
    return _admin


# ---------------------------------------------------------------------------
# Standalone decorator — no client setup needed
# ---------------------------------------------------------------------------


def require_approval(
    fn: Callable | None = None,
    *,
    action: str | None = None,
    requested_by: str | None = None,
    callback_url: str | None = None,
) -> Callable:
    """Standalone decorator that gates a function behind human approval.

    Reads ``PRIMITIF_API_KEY`` from the environment. No client setup needed.
    Creates a new Primitif client on each call.

    Can be used with or without parentheses: ``@require_approval`` or
    ``@require_approval(action="Deploy")``.

    Args:
        fn: The function to wrap. Passed automatically when used as
            ``@require_approval`` without parentheses. Do not pass explicitly.
        action: Human-readable action name shown to the approver.
            Defaults to the function name with underscores replaced by
            spaces (``send_invoice`` -> ``"Send invoice"``).
        requested_by: Optional agent or service identifier attached to the
            approval request. Defaults to None.
        callback_url: Optional webhook URL for direct callback when the
            request is decided. Defaults to None.

    Returns:
        The decorated function. Each call returns an ApprovalRequest
        (status ``"pending"``) instead of executing the original function.
        The original is accessible via ``wrapped.original``.

    Raises:
        PrimitifError: If ``PRIMITIF_API_KEY`` is not set.

    Example:
        >>> from primitif.approval import require_approval
        >>> @require_approval
        ... def deploy(service, branch, env):
        ...     run_pipeline(service, branch, env)
        >>> req = deploy("api-gateway", "main", "production")
        >>> print(req.approval_url)  # share with the human

        >>> @require_approval(requested_by="deploy-agent")
        ... def deploy(service, branch, env):
        ...     run_pipeline(service, branch, env)
    """

    def decorator(fn: Callable) -> Callable:
        resolved_action = action or fn.__name__.replace("_", " ").capitalize()

        @functools.wraps(fn)
        def wrapper(*args: Any, **kwargs: Any) -> ApprovalRequest:
            from primitif.client import Primitif

            p = Primitif()
            sig = inspect.signature(fn)
            bound = sig.bind(*args, **kwargs)
            bound.apply_defaults()
            context = dict(bound.arguments)

            return p.approval.create_request(
                resolved_action,
                context=context,
                requested_by=requested_by,
                callback_url=callback_url,
            )

        wrapper.original = fn  # type: ignore[attr-defined]
        return wrapper

    # Support both @require_approval and @require_approval(...)
    if fn is not None:
        return decorator(fn)
    return decorator


# ---------------------------------------------------------------------------
# Module-level functions — the simple import path
# ---------------------------------------------------------------------------


def create_request(
    action: str,
    *,
    context: dict[str, Any] | None = None,
    callback_url: str | None = None,
    requested_by: str | None = None,
) -> ApprovalRequest:
    """Create an approval request. Returns immediately with a pending status.

    Args:
        action: Human-readable description of the action awaiting
            approval (e.g. ``"Send contract to Acme"``).
        context: Arbitrary JSON dict passed to the approver and
            included in the webhook payload.
        callback_url: Optional URL for a direct webhook callback when
            the request is decided.
        requested_by: Optional agent or service identifier.

    Example:
        >>> from primitif import approval
        >>> req = approval.create_request(
        ...     "Send contract",
        ...     context={"client": "Acme", "amount": 50000},
        ... )
        >>> print(req.approval_url)
    """
    return _get_admin().create_request(
        action,
        context=context,
        callback_url=callback_url,
        requested_by=requested_by,
    )


def get_request(request_id: str) -> ApprovalRequest:
    """Get the current status of an approval request.

    Args:
        request_id: The approval request ID.

    Example:
        >>> status = approval.get_request("apr_abc123")
        >>> print(status.status)
    """
    return _get_admin().get_request(request_id)


def list_requests(
    *,
    status: Literal["pending", "approved", "rejected", "expired"] | None = None,
    limit: int = 20,
    cursor: str | None = None,
) -> PaginatedList[ApprovalRequest]:
    """List approval requests, optionally filtered by status.

    Args:
        status: Filter by status. Omit to list all.
        limit: Maximum number of requests per page. Defaults to 20.
        cursor: Pagination cursor from a previous response.

    Example:
        >>> for req in approval.list_requests(status="pending"):
        ...     print(req.action, req.approval_url)
    """
    return _get_admin().list_requests(status=status, limit=limit, cursor=cursor)


__all__ = [
    # Module-level functions
    "create_request",
    "get_request",
    "list_requests",
    "require_approval",
    # Classes
    "ApprovalAdmin",
    # Exceptions
    "ApprovalError",
    "ApprovalAuthError",
    "ApprovalNetworkError",
    "ApprovalNotFoundError",
    "ApprovalValidationError",
    "ApprovalRateLimitError",
    "ApprovalServerError",
    # Models
    "ApprovalRequest",
]
