"""Tests for the PyAuthz Django integration."""

import json

import django
from django.conf import settings

# Configure Django settings before anything else
if not settings.configured:
    settings.configure(
        ROOT_URLCONF="tests_django.test_django",
        MIDDLEWARE=["pyauthz_django.PyAuthzMiddleware"],
        SECRET_KEY="test-secret-key",
        ALLOWED_HOSTS=["*"],
        DEFAULT_AUTO_FIELD="django.db.models.BigAutoField",
    )
    django.setup()

from django.http import JsonResponse
from django.test import RequestFactory
from django.urls import path

from pyauthz import (
    Condition,
    Effect,
    Policy,
    PyAuthz,
    Resource,
    Role,
    Subject,
)
from pyauthz_django.decorator import requires
from pyauthz_django.middleware import PyAuthzMiddleware, configure_pyauthz

# --- Test views ---


def index_view(request):
    return JsonResponse({"ok": True})


@requires("documents:read")
def read_view(request):
    return JsonResponse({"ok": True})


@requires("documents:delete")
def delete_view(request):
    return JsonResponse({"deleted": True})


@requires("documents:write")
def write_view(request):
    return JsonResponse({"updated": True})


urlpatterns = [
    path("index/", index_view),
    path("read/", read_view),
    path("delete/", delete_view),
    path("write/", write_view),
]


class TestDjangoMiddleware:
    def _get_response(self, view_fn):
        """Create a middleware-wrapped view handler."""

        def get_response(request):
            return view_fn(request)

        return get_response

    def setup_method(self):
        """Reset module-level state before each test."""
        from pyauthz_django import middleware

        middleware._pyauthz_instance = None
        middleware._subject_resolver = None

    def test_middleware_passes_through_without_config(self):
        """Middleware should pass through if not configured."""
        factory = RequestFactory()
        request = factory.get("/index/")
        mw = PyAuthzMiddleware(self._get_response(index_view))
        response = mw(request)
        assert response.status_code == 200

    def test_allowed_request(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        admin = Subject(id="alice", roles=["admin"])

        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: admin)

        factory = RequestFactory()
        request = factory.get("/read/")
        mw = PyAuthzMiddleware(self._get_response(read_view))
        response = mw(request)
        assert response.status_code == 200
        assert json.loads(response.content) == {"ok": True}

    def test_denied_request_returns_403(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))
        viewer = Subject(id="bob", roles=["viewer"])

        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: viewer)

        factory = RequestFactory()
        request = factory.delete("/delete/")
        mw = PyAuthzMiddleware(self._get_response(delete_view))
        response = mw(request)
        assert response.status_code == 403
        body = json.loads(response.content)
        assert body["error"] == "forbidden"
        assert "reasoning" in body

    def test_subject_attached_to_request(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        admin = Subject(id="alice", roles=["admin"])

        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: admin)

        def check_subject_view(request):
            return JsonResponse(
                {
                    "subject_id": request.pyauthz_subject.id,
                    "has_pyauthz": hasattr(request, "pyauthz"),
                }
            )

        factory = RequestFactory()
        request = factory.get("/")
        mw = PyAuthzMiddleware(self._get_response(check_subject_view))
        response = mw(request)
        body = json.loads(response.content)
        assert body["subject_id"] == "alice"
        assert body["has_pyauthz"] is True

    def test_header_based_resolver(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))

        def resolve_from_headers(request):
            user_id = request.META.get("HTTP_X_USER_ID", "anonymous")
            roles_header = request.META.get("HTTP_X_USER_ROLES", "")
            roles = roles_header.split(",") if roles_header else []
            return Subject(id=user_id, roles=roles)

        configure_pyauthz(pyauthz=rp, subject_resolver=resolve_from_headers)

        factory = RequestFactory()
        request = factory.get(
            "/read/",
            HTTP_X_USER_ID="carol",
            HTTP_X_USER_ROLES="viewer",
        )
        mw = PyAuthzMiddleware(self._get_response(read_view))
        response = mw(request)
        assert response.status_code == 200

    def test_anonymous_denied(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))

        def resolve_from_headers(request):
            user_id = request.META.get("HTTP_X_USER_ID", "anonymous")
            roles_header = request.META.get("HTTP_X_USER_ROLES", "")
            roles = roles_header.split(",") if roles_header else []
            return Subject(id=user_id, roles=roles)

        configure_pyauthz(pyauthz=rp, subject_resolver=resolve_from_headers)

        factory = RequestFactory()
        request = factory.get("/read/")
        mw = PyAuthzMiddleware(self._get_response(read_view))
        response = mw(request)
        assert response.status_code == 403


class TestDjangoDecorator:
    def setup_method(self):
        from pyauthz_django import middleware

        middleware._pyauthz_instance = None
        middleware._subject_resolver = None

    def _get_response(self, view_fn):
        def get_response(request):
            return view_fn(request)

        return get_response

    def test_requires_with_resource_resolver(self):
        rp = PyAuthz()
        rp.add_role(Role(name="editor", permissions=["documents:write"]))
        rp.add_policy(
            Policy(
                name="owner-edit",
                effect=Effect.ALLOW,
                roles=["editor"],
                permissions=["documents:write"],
                conditions=[
                    Condition(field="resource.owner_id", op="==", value="subject.id"),
                ],
            )
        )
        alice = Subject(id="alice", roles=["editor"])
        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: alice)

        def resolve_document(request, **kwargs) -> Resource:
            return Resource(id="doc-1", type="documents", owner_id="alice")

        @requires("documents:write", resource_resolver=resolve_document)
        def owner_write_view(request):
            return JsonResponse({"updated": True})

        factory = RequestFactory()
        request = factory.put("/write/")
        mw = PyAuthzMiddleware(self._get_response(owner_write_view))
        response = mw(request)
        assert response.status_code == 200

    def test_requires_non_owner_denied(self):
        rp = PyAuthz()
        rp.add_role(Role(name="editor", permissions=["documents:write"]))
        rp.add_policy(
            Policy(
                name="owner-edit",
                effect=Effect.ALLOW,
                roles=["editor"],
                permissions=["documents:write"],
                conditions=[
                    Condition(field="resource.owner_id", op="==", value="subject.id"),
                ],
            )
        )
        bob = Subject(id="bob", roles=["editor"])
        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: bob)

        def resolve_document(request, **kwargs) -> Resource:
            return Resource(id="doc-1", type="documents", owner_id="alice")

        @requires("documents:write", resource_resolver=resolve_document)
        def owner_write_view(request):
            return JsonResponse({"updated": True})

        factory = RequestFactory()
        request = factory.put("/write/")
        mw = PyAuthzMiddleware(self._get_response(owner_write_view))
        response = mw(request)
        assert response.status_code == 403

    def test_decision_attached_to_request(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        admin = Subject(id="alice", roles=["admin"])
        configure_pyauthz(pyauthz=rp, subject_resolver=lambda req: admin)

        @requires("documents:read")
        def view_with_decision(request):
            decision = getattr(request, "pyauthz_decision", None)
            return JsonResponse({"has_decision": decision is not None})

        factory = RequestFactory()
        request = factory.get("/")
        mw = PyAuthzMiddleware(self._get_response(view_with_decision))
        response = mw(request)
        body = json.loads(response.content)
        assert body["has_decision"] is True
