"""Tests for SortSpec list-like collection."""

from therismos.sorting import SortCriterion, SortOrder, SortSpec


class TestSortSpecConstruction:
    """Tests for SortSpec construction and basic operations."""

    def test_create_empty_sort_spec(self):
        """Test creating an empty sort specification."""
        spec = SortSpec()
        assert len(spec) == 0
        assert list(spec) == []

    def test_create_sort_spec_with_criteria(self):
        """Test creating a sort spec with initial criteria."""
        criteria = [
            SortCriterion("age", SortOrder.ASCENDING),
            SortCriterion("name", SortOrder.DESCENDING),
        ]
        spec = SortSpec(criteria)

        assert len(spec) == 2
        assert spec[0].field == "age"
        assert spec[0].order == SortOrder.ASCENDING
        assert spec[1].field == "name"
        assert spec[1].order == SortOrder.DESCENDING


class TestSortSpecListOperations:
    """Tests for list-like operations on SortSpec."""

    def test_append_criterion(self):
        """Test appending a criterion to sort spec."""
        spec = SortSpec()
        criterion = SortCriterion("age", SortOrder.ASCENDING)

        spec.append(criterion)

        assert len(spec) == 1
        assert spec[0] is criterion

    def test_extend_with_criteria(self):
        """Test extending sort spec with multiple criteria."""
        spec = SortSpec([SortCriterion("age", SortOrder.ASCENDING)])

        new_criteria = [
            SortCriterion("name", SortOrder.DESCENDING),
            SortCriterion("score", SortOrder.ASCENDING),
        ]
        spec.extend(new_criteria)

        assert len(spec) == 3
        assert spec[1].field == "name"
        assert spec[2].field == "score"

    def test_indexing(self):
        """Test accessing criteria by index."""
        criteria = [
            SortCriterion("age", SortOrder.ASCENDING),
            SortCriterion("name", SortOrder.DESCENDING),
        ]
        spec = SortSpec(criteria)

        assert spec[0].field == "age"
        assert spec[1].field == "name"
        assert spec[-1].field == "name"
        assert spec[-2].field == "age"

    def test_slicing(self):
        """Test slicing sort spec."""
        criteria = [
            SortCriterion("a", SortOrder.ASCENDING),
            SortCriterion("b", SortOrder.DESCENDING),
            SortCriterion("c", SortOrder.ASCENDING),
        ]
        spec = SortSpec(criteria)

        slice_result = spec[1:]
        assert len(slice_result) == 2
        assert slice_result[0].field == "b"
        assert slice_result[1].field == "c"

    def test_iteration(self):
        """Test iterating over sort spec."""
        criteria = [
            SortCriterion("age", SortOrder.ASCENDING),
            SortCriterion("name", SortOrder.DESCENDING),
        ]
        spec = SortSpec(criteria)

        field_names = [criterion.field for criterion in spec]
        assert field_names == ["age", "name"]

    def test_contains(self):
        """Test checking if criterion is in sort spec."""
        criterion1 = SortCriterion("age", SortOrder.ASCENDING)
        criterion2 = SortCriterion("name", SortOrder.DESCENDING)
        criterion3 = SortCriterion("score", SortOrder.ASCENDING)

        spec = SortSpec([criterion1, criterion2])

        assert criterion1 in spec
        assert criterion2 in spec
        assert criterion3 not in spec

    def test_remove_criterion(self):
        """Test removing a criterion from sort spec."""
        criterion1 = SortCriterion("age", SortOrder.ASCENDING)
        criterion2 = SortCriterion("name", SortOrder.DESCENDING)

        spec = SortSpec([criterion1, criterion2])
        spec.remove(criterion1)

        assert len(spec) == 1
        assert spec[0] is criterion2

    def test_insert_criterion(self):
        """Test inserting a criterion at specific position."""
        spec = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("score", SortOrder.DESCENDING),
            ]
        )

        new_criterion = SortCriterion("name", SortOrder.ASCENDING)
        spec.insert(1, new_criterion)

        assert len(spec) == 3
        assert spec[1].field == "name"

    def test_clear_spec(self):
        """Test clearing all criteria from sort spec."""
        spec = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("name", SortOrder.DESCENDING),
            ]
        )

        spec.clear()

        assert len(spec) == 0


class TestSortSpecEquality:
    """Tests for SortSpec equality comparisons."""

    def test_equal_specs(self):
        """Test that specs with same criteria are equal."""
        spec1 = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("name", SortOrder.DESCENDING),
            ]
        )
        spec2 = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("name", SortOrder.DESCENDING),
            ]
        )

        assert spec1 == spec2

    def test_unequal_specs_different_order(self):
        """Test that specs with different criterion order are not equal."""
        spec1 = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("name", SortOrder.DESCENDING),
            ]
        )
        spec2 = SortSpec(
            [
                SortCriterion("name", SortOrder.DESCENDING),
                SortCriterion("age", SortOrder.ASCENDING),
            ]
        )

        assert spec1 != spec2

    def test_unequal_specs_different_length(self):
        """Test that specs with different lengths are not equal."""
        spec1 = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
            ]
        )
        spec2 = SortSpec(
            [
                SortCriterion("age", SortOrder.ASCENDING),
                SortCriterion("name", SortOrder.DESCENDING),
            ]
        )

        assert spec1 != spec2
