"""Flask extension for PyAuthz authorization.

PyAuthzExtension hooks into Flask's before_request to resolve the subject
and attach it to Flask's `g` object. It also registers an error handler
for Denied exceptions, returning structured 403 JSON responses.
"""

from __future__ import annotations

from collections.abc import Callable
from typing import Any

from flask import Flask, Request, g, jsonify

from pyauthz import PyAuthz, Subject
from pyauthz.core.errors import Denied

# Type for the subject resolver: takes a Flask Request, returns a Subject.
SubjectResolver = Callable[[Request], Subject]


class PyAuthzExtension:
    """Flask extension that integrates PyAuthz authorization.

    Resolves the Subject before each request and registers a Denied
    exception handler that returns 403 JSON responses.

    Example:
        app = Flask(__name__)
        ext = PyAuthzExtension(app, pyauthz=rp, subject_resolver=resolve_user)
    """

    def __init__(
        self,
        app: Flask | None = None,
        *,
        pyauthz: PyAuthz | None = None,
        subject_resolver: SubjectResolver | None = None,
    ) -> None:
        self._pyauthz = pyauthz
        self._subject_resolver = subject_resolver
        if app is not None:
            self.init_app(app, pyauthz=pyauthz, subject_resolver=subject_resolver)

    def init_app(
        self,
        app: Flask,
        *,
        pyauthz: PyAuthz | None = None,
        subject_resolver: SubjectResolver | None = None,
    ) -> None:
        """Initialize the extension with a Flask app.

        Supports the Flask factory pattern.

        Args:
            app: The Flask application.
            pyauthz: The PyAuthz engine instance.
            subject_resolver: Callable that extracts a Subject from the request.
        """
        rp = pyauthz or self._pyauthz
        resolver = subject_resolver or self._subject_resolver

        if rp is None:
            raise ValueError("A PyAuthz instance is required")
        if resolver is None:
            raise ValueError("A subject_resolver is required")

        # Store on the app for retrieval via get_pyauthz()
        app.extensions["pyauthz"] = rp
        app.extensions["pyauthz_subject_resolver"] = resolver

        @app.before_request
        def _resolve_subject() -> None:
            from flask import request

            g.pyauthz = rp
            g.pyauthz_subject = resolver(request)

        @app.errorhandler(Denied)
        def _handle_denied(exc: Denied) -> tuple[Any, int]:
            decision = exc.decision
            return jsonify(
                {
                    "error": "forbidden",
                    "detail": decision.explain(),
                    "matched_policies": decision.matched_policies,
                    "reasoning": decision.reasoning,
                }
            ), 403


def get_subject() -> Subject:
    """Get the current request's Subject from Flask's g object.

    Must be called within a request context after PyAuthzExtension is configured.

    Returns:
        The resolved Subject for the current request.
    """
    return g.pyauthz_subject


def get_pyauthz() -> PyAuthz:
    """Get the PyAuthz engine instance from Flask's g object.

    Returns:
        The PyAuthz engine instance.
    """
    return g.pyauthz
