"""Tests for policy diffing."""

from pyauthz import (
    PyAuthz,
    Resource,
    Role,
    Subject,
    diff_policies,
)


class TestDiffPolicies:
    def test_no_changes(self):
        rp1 = PyAuthz()
        rp1.add_role(Role(name="admin", permissions=["*:*"]))

        rp2 = PyAuthz()
        rp2.add_role(Role(name="admin", permissions=["*:*"]))

        admin = Subject(id="alice", roles=["admin"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(rp1, rp2, [(admin, "documents:read", doc)])
        assert not report.has_changes
        assert report.unchanged_count == 1
        assert report.total_scenarios == 1
        assert "No changes" in report.summary()

    def test_allow_to_deny(self):
        rp_old = PyAuthz()
        rp_old.add_role(Role(name="editor", permissions=["documents:write"]))

        rp_new = PyAuthz()
        rp_new.add_role(Role(name="editor", permissions=["documents:read"]))

        editor = Subject(id="alice", roles=["editor"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(rp_old, rp_new, [(editor, "documents:write", doc)])
        assert report.has_changes
        assert len(report.changes) == 1
        assert report.changes[0].change_type == "allow→deny"
        assert "allow→deny" in report.summary()

    def test_deny_to_allow(self):
        rp_old = PyAuthz()
        rp_old.add_role(Role(name="editor", permissions=["documents:read"]))

        rp_new = PyAuthz()
        rp_new.add_role(Role(name="editor", permissions=["documents:write"]))

        editor = Subject(id="alice", roles=["editor"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(rp_old, rp_new, [(editor, "documents:write", doc)])
        assert report.has_changes
        assert report.changes[0].change_type == "deny→allow"

    def test_multiple_scenarios(self):
        rp_old = PyAuthz()
        rp_old.add_role(Role(name="admin", permissions=["*:*"]))
        rp_old.add_role(Role(name="viewer", permissions=["documents:read"]))

        rp_new = PyAuthz()
        rp_new.add_role(Role(name="admin", permissions=["documents:read"]))
        rp_new.add_role(Role(name="viewer", permissions=["documents:read", "documents:write"]))

        admin = Subject(id="alice", roles=["admin"])
        viewer = Subject(id="bob", roles=["viewer"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(
            rp_old,
            rp_new,
            [
                (admin, "documents:write", doc),  # was allowed, now denied
                (viewer, "documents:write", doc),  # was denied, now allowed
                (viewer, "documents:read", doc),  # still allowed
            ],
        )
        assert report.total_scenarios == 3
        assert len(report.changes) == 2
        assert report.unchanged_count == 1

    def test_include_unchanged(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))

        admin = Subject(id="alice", roles=["admin"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(rp, rp, [(admin, "documents:read", doc)], include_unchanged=True)
        assert len(report.changes) == 1
        assert report.changes[0].change_type == "unchanged"

    def test_policy_change_str(self):
        rp_old = PyAuthz()
        rp_old.add_role(Role(name="admin", permissions=["*:*"]))

        rp_new = PyAuthz()
        rp_new.add_role(Role(name="admin", permissions=["documents:read"]))

        admin = Subject(id="alice", roles=["admin"])
        doc = Resource(id="doc-1", type="documents")

        report = diff_policies(rp_old, rp_new, [(admin, "documents:write", doc)])
        change_str = str(report.changes[0])
        assert "alice" in change_str
        assert "ALLOW" in change_str
        assert "DENY" in change_str
