# Copyright Visual Collaboration Technologies Inc. All Rights Reserved.
# See LICENSE for details.
"""Tests for PolicyBoundDeck."""

from enum import StrEnum, auto

import pytest

from vcti.deck import CompatibilityPolicy, PolicyBoundDeck


class Kind(StrEnum):
    ALPHA = auto()
    BETA = auto()


class Item:
    def __init__(self, kind: Kind):
        self.kind = kind


class SameKindPolicy(CompatibilityPolicy[Kind, Item]):
    def is_compatible(self, item: Item, deck_type: Kind) -> bool:
        return item.kind == deck_type


class AcceptAllPolicy(CompatibilityPolicy[str, int]):
    def is_compatible(self, item: int, deck_type: str) -> bool:
        return True


class RejectAllPolicy(CompatibilityPolicy[str, int]):
    def is_compatible(self, item: int, deck_type: str) -> bool:
        return False


class TestPolicyBoundDeckBasic:
    def test_compatible_item_accepted(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        item = Item(Kind.ALPHA)
        deck.add("a", item)
        assert deck.get("a") is item

    def test_incompatible_item_rejected(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        item = Item(Kind.BETA)
        with pytest.raises(ValueError, match="Incompatible"):
            deck.add("b", item)

    def test_accept_all_policy(self):
        deck = PolicyBoundDeck(deck_type="any", policy=AcceptAllPolicy())
        deck.add("a", 1)
        deck.add("b", 2)
        assert len(deck) == 2

    def test_reject_all_policy(self):
        deck = PolicyBoundDeck(deck_type="none", policy=RejectAllPolicy())
        with pytest.raises(ValueError, match="Incompatible"):
            deck.add("a", 1)


class TestPolicyBoundDeckAttributes:
    def test_deck_type_stored(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        assert deck.deck_type == Kind.ALPHA

    def test_policy_stored(self):
        policy = SameKindPolicy()
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=policy)
        assert deck.policy is policy


class TestPolicyBoundDeckInheritsOrdering:
    def test_custom_order(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        deck.add("a", Item(Kind.ALPHA))
        deck.add("b", Item(Kind.ALPHA))
        deck.add("c", Item(Kind.ALPHA))
        deck.set_order(["c", "a", "b"])
        assert deck.ids() == ["c", "a", "b"]

    def test_remove(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        deck.add("a", Item(Kind.ALPHA))
        deck.remove("a")
        assert len(deck) == 0

    def test_replace(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        item1 = Item(Kind.ALPHA)
        item2 = Item(Kind.ALPHA)
        deck.add("a", item1)
        deck.add("a", item2, replace=True)
        assert deck.get("a") is item2


class TestCompatibilityPolicyIsAbstract:
    def test_cannot_instantiate_base(self):
        with pytest.raises(TypeError, match="abstract"):
            CompatibilityPolicy()

    def test_incomplete_subclass_cannot_instantiate(self):
        class Incomplete(CompatibilityPolicy[str, int]):
            pass

        with pytest.raises(TypeError, match="abstract"):
            Incomplete()

    def test_replace_still_checks_compatibility(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        deck.add("a", Item(Kind.ALPHA))
        with pytest.raises(ValueError, match="Incompatible"):
            deck.add("a", Item(Kind.BETA), replace=True)


class TestPolicyBoundDeckRepr:
    def test_repr(self):
        deck = PolicyBoundDeck(deck_type=Kind.ALPHA, policy=SameKindPolicy())
        deck.add("a", Item(Kind.ALPHA))
        r = repr(deck)
        assert "PolicyBoundDeck" in r
        assert "alpha" in r
        assert "items=1" in r
