"""Tests for policy combination algorithms."""

from pyauthz import Effect, Policy
from pyauthz.policies.combiners import allow_override, deny_override, first_match, priority_first


class TestDenyOverride:
    def test_empty_list(self):
        effect, names, reasoning = deny_override([])
        assert effect == Effect.DENY
        assert names == []
        assert "no matching policy" in reasoning[0]

    def test_single_allow(self):
        effect, names, reasoning = deny_override(
            [
                Policy(name="a", effect=Effect.ALLOW),
            ]
        )
        assert effect == Effect.ALLOW
        assert names == ["a"]

    def test_single_deny(self):
        effect, names, reasoning = deny_override(
            [
                Policy(name="d", effect=Effect.DENY),
            ]
        )
        assert effect == Effect.DENY

    def test_deny_wins_over_allow(self):
        effect, names, reasoning = deny_override(
            [
                Policy(name="a", effect=Effect.ALLOW),
                Policy(name="d", effect=Effect.DENY),
            ]
        )
        assert effect == Effect.DENY
        assert "a" in names
        assert "d" in names

    def test_multiple_allows(self):
        effect, names, _ = deny_override(
            [
                Policy(name="a1", effect=Effect.ALLOW),
                Policy(name="a2", effect=Effect.ALLOW),
            ]
        )
        assert effect == Effect.ALLOW
        assert len(names) == 2


class TestAllowOverride:
    def test_empty_list(self):
        effect, names, reasoning = allow_override([])
        assert effect == Effect.DENY
        assert "no matching policy" in reasoning[0]

    def test_single_allow(self):
        effect, names, _ = allow_override([Policy(name="a", effect=Effect.ALLOW)])
        assert effect == Effect.ALLOW

    def test_single_deny(self):
        effect, names, _ = allow_override([Policy(name="d", effect=Effect.DENY)])
        assert effect == Effect.DENY

    def test_allow_wins_over_deny(self):
        effect, names, _ = allow_override(
            [
                Policy(name="d", effect=Effect.DENY),
                Policy(name="a", effect=Effect.ALLOW),
            ]
        )
        assert effect == Effect.ALLOW
        assert "d" in names
        assert "a" in names

    def test_multiple_denies(self):
        effect, names, _ = allow_override(
            [
                Policy(name="d1", effect=Effect.DENY),
                Policy(name="d2", effect=Effect.DENY),
            ]
        )
        assert effect == Effect.DENY


class TestPriorityFirst:
    def test_empty_list(self):
        effect, names, reasoning = priority_first([])
        assert effect == Effect.DENY
        assert "no matching policy" in reasoning[0]

    def test_highest_priority_wins(self):
        effect, names, reasoning = priority_first(
            [
                Policy(name="low", effect=Effect.ALLOW, priority=1),
                Policy(name="high", effect=Effect.DENY, priority=100),
            ]
        )
        assert effect == Effect.DENY
        assert "high" in reasoning[0]

    def test_same_priority_first_wins(self):
        """When priorities are equal, the first in evaluation order wins."""
        effect, names, reasoning = priority_first(
            [
                Policy(name="first", effect=Effect.ALLOW, priority=10),
                Policy(name="second", effect=Effect.DENY, priority=10),
            ]
        )
        assert effect == Effect.ALLOW
        assert "first" in reasoning[0]

    def test_all_policies_in_matched_names(self):
        effect, names, _ = priority_first(
            [
                Policy(name="a", effect=Effect.ALLOW, priority=1),
                Policy(name="b", effect=Effect.DENY, priority=50),
                Policy(name="c", effect=Effect.ALLOW, priority=10),
            ]
        )
        assert len(names) == 3


class TestFirstMatch:
    def test_empty_list(self):
        effect, names, reasoning = first_match([])
        assert effect == Effect.DENY
        assert names == []

    def test_first_allow(self):
        effect, names, _ = first_match(
            [
                Policy(name="a", effect=Effect.ALLOW),
                Policy(name="d", effect=Effect.DENY),
            ]
        )
        assert effect == Effect.ALLOW
        assert names == ["a"]

    def test_first_deny(self):
        effect, names, _ = first_match(
            [
                Policy(name="d", effect=Effect.DENY),
                Policy(name="a", effect=Effect.ALLOW),
            ]
        )
        assert effect == Effect.DENY
        assert names == ["d"]
