"""Tests for I-Logic Algebra"""
import pytest
from cubesquare.i_logic import ILogicState
from cubesquare.i_logic.algebra import multiply, compose, detect_cycle, get_inverse, verify_algebra


class TestILogicStates:
    """Test I-Logic state definitions."""

    def test_states_exist(self):
        I = ILogicState
        assert I.DIRECT == 1 + 0j
        assert I.INVERSE == -1 + 0j
        assert I.SPIRAL == 0 + 1j
        assert I.ANTI_SPIRAL == 0 - 1j
        assert I.ABSORBED == 0 + 0j

    def test_from_name(self):
        assert ILogicState.from_name("DIRECT") == ILogicState.DIRECT
        assert ILogicState.from_name("inverse") == ILogicState.INVERSE
        assert ILogicState.from_name("SPIRAL") == ILogicState.SPIRAL
        assert ILogicState.from_name("1") == ILogicState.DIRECT
        assert ILogicState.from_name("-1") == ILogicState.INVERSE

    def test_name(self):
        assert ILogicState.name(ILogicState.DIRECT) == "DIRECT"
        assert ILogicState.name(ILogicState.INVERSE) == "INVERSE"
        assert ILogicState.name(ILogicState.SPIRAL) == "SPIRAL"

    def test_symbol(self):
        assert ILogicState.symbol(ILogicState.DIRECT) == "1"
        assert ILogicState.symbol(ILogicState.INVERSE) == "-1"
        assert ILogicState.symbol(ILogicState.SPIRAL) == "+i"


class TestMultiplication:
    """Test O(1) multiplication."""

    def test_identity(self):
        I = ILogicState
        # DIRECT is identity
        for state in I.ALL_STATES:
            assert multiply(I.DIRECT, state) == state
            assert multiply(state, I.DIRECT) == state

    def test_absorbing(self):
        I = ILogicState
        # ABSORBED absorbs everything
        for state in I.ALL_STATES:
            assert multiply(I.ABSORBED, state) == I.ABSORBED
            assert multiply(state, I.ABSORBED) == I.ABSORBED

    def test_inverse_inverse(self):
        I = ILogicState
        # INVERSE * INVERSE = DIRECT
        assert multiply(I.INVERSE, I.INVERSE) == I.DIRECT

    def test_spiral_spiral(self):
        I = ILogicState
        # SPIRAL * SPIRAL = INVERSE (i * i = -1)
        assert multiply(I.SPIRAL, I.SPIRAL) == I.INVERSE

    def test_spiral_anti_spiral(self):
        I = ILogicState
        # SPIRAL * ANTI_SPIRAL = DIRECT (i * -i = 1)
        assert multiply(I.SPIRAL, I.ANTI_SPIRAL) == I.DIRECT


class TestComposition:
    """Test O(N) path composition."""

    def test_empty_path(self):
        result, steps = compose([])
        assert result == ILogicState.DIRECT
        assert steps == 0

    def test_single_state(self):
        result, steps = compose([ILogicState.INVERSE])
        assert result == ILogicState.INVERSE
        assert steps == 1

    def test_early_termination(self):
        I = ILogicState
        # Path with ABSORBED terminates early
        result, steps = compose([I.DIRECT, I.ABSORBED, I.SPIRAL, I.SPIRAL])
        assert result == I.ABSORBED
        assert steps == 2  # Terminated at ABSORBED

    def test_cycle_4(self):
        I = ILogicState
        # SPIRAL^4 = DIRECT (period 4)
        result, steps = compose([I.SPIRAL, I.SPIRAL, I.SPIRAL, I.SPIRAL])
        assert result == I.DIRECT
        assert steps == 4


class TestCycleDetection:
    """Test cycle detection."""

    def test_terminates(self):
        I = ILogicState
        is_cycle, desc, period = detect_cycle([I.DIRECT, I.ABSORBED])
        assert not is_cycle
        assert "terminates" in desc

    def test_detects_cycle(self):
        I = ILogicState
        # INVERSE * INVERSE = DIRECT (cycle of length 2)
        is_cycle, desc, period = detect_cycle([I.INVERSE, I.INVERSE])
        assert is_cycle
        assert period == 2


class TestInverse:
    """Test state inverses."""

    def test_direct_inverse(self):
        assert get_inverse(ILogicState.DIRECT) == ILogicState.DIRECT

    def test_inverse_inverse(self):
        assert get_inverse(ILogicState.INVERSE) == ILogicState.INVERSE

    def test_spiral_inverse(self):
        assert get_inverse(ILogicState.SPIRAL) == ILogicState.ANTI_SPIRAL

    def test_absorbed_no_inverse(self):
        assert get_inverse(ILogicState.ABSORBED) is None


class TestAlgebraVerification:
    """Test full algebra verification."""

    def test_verify_all_properties(self):
        """This tests all 5 algebraic properties with 125+ combinations."""
        assert verify_algebra() is True


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
