"""Tests for the pyauthz.testing module."""

import pytest

from pyauthz import PyAuthz, Role
from pyauthz.testing import AuthScenario, must_allow, must_deny


class TestMustAllow:
    def test_passes_on_allow(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        with AuthScenario(rp) as s:
            user = s.subject("alice", roles=["admin"])
            doc = s.resource("doc-1", type="documents")
            decision = s.assert_that(user, "documents:read", doc, must_allow)
            assert decision.allowed

    def test_fails_on_deny(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))
        with AuthScenario(rp) as s:
            user = s.subject("bob", roles=["viewer"])
            doc = s.resource("doc-1", type="documents")
            with pytest.raises(AssertionError, match="expected ALLOW but got DENY"):
                s.assert_that(user, "documents:write", doc, must_allow)


class TestMustDeny:
    def test_passes_on_deny(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))
        with AuthScenario(rp) as s:
            user = s.subject("bob", roles=["viewer"])
            doc = s.resource("doc-1", type="documents")
            decision = s.assert_that(user, "documents:write", doc, must_deny)
            assert not decision.allowed

    def test_fails_on_allow(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        with AuthScenario(rp) as s:
            user = s.subject("alice", roles=["admin"])
            doc = s.resource("doc-1", type="documents")
            with pytest.raises(AssertionError, match="expected DENY but got ALLOW"):
                s.assert_that(user, "documents:read", doc, must_deny)


class TestAuthScenario:
    def test_subject_with_attributes(self):
        rp = PyAuthz()
        with AuthScenario(rp) as s:
            user = s.subject("alice", roles=["admin"], department="engineering")
            assert user.id == "alice"
            assert user.roles == ["admin"]
            assert user.attributes["department"] == "engineering"

    def test_resource_with_attributes(self):
        rp = PyAuthz()
        with AuthScenario(rp) as s:
            doc = s.resource("doc-1", type="documents", owner_id="alice", classification="secret")
            assert doc.id == "doc-1"
            assert doc.type == "documents"
            assert doc.owner_id == "alice"
            assert doc.attributes["classification"] == "secret"

    def test_evaluate_returns_decision(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        with AuthScenario(rp) as s:
            user = s.subject("alice", roles=["admin"])
            doc = s.resource("doc-1", type="documents")
            decision = s.evaluate(user, "documents:read", doc)
            assert decision.allowed

    def test_results_tracking(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        with AuthScenario(rp) as s:
            user = s.subject("alice", roles=["admin"])
            doc = s.resource("doc-1", type="documents")
            s.assert_that(user, "documents:read", doc, must_allow)
            assert len(s.results) == 1
            assert s.results[0][1] is True  # passed

    def test_assert_bulk_all_pass(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))
        with AuthScenario(rp) as s:
            admin = s.subject("alice", roles=["admin"])
            viewer = s.subject("bob", roles=["viewer"])
            doc = s.resource("doc-1", type="documents")
            decisions = s.assert_bulk(
                [
                    (admin, "documents:read", doc, must_allow),
                    (viewer, "documents:write", doc, must_deny),
                ]
            )
            assert len(decisions) == 2

    def test_assert_bulk_collects_failures(self):
        rp = PyAuthz()
        rp.add_role(Role(name="viewer", permissions=["documents:read"]))
        with AuthScenario(rp) as s:
            viewer = s.subject("bob", roles=["viewer"])
            doc = s.resource("doc-1", type="documents")
            with pytest.raises(AssertionError, match="2 authorization assertion"):
                s.assert_bulk(
                    [
                        (viewer, "documents:write", doc, must_allow),  # will fail
                        (viewer, "documents:delete", doc, must_allow),  # will fail
                    ]
                )

    def test_environment_context(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        with AuthScenario(rp, region="us-east-1") as s:
            user = s.subject("alice", roles=["admin"])
            doc = s.resource("doc-1", type="documents")
            decision = s.evaluate(user, "documents:read", doc)
            assert decision.allowed
