# tests/v1/test_frozen_tuple_immutability.py

"""Tests to verify that tuple elements are recursively frozen.

This test file ensures that the previous bug - where tuple contents remained
mutable despite freezing - has been fixed. Before the fix, tuples were passed
through freeze_value() without recursively freezing their mutable elements,
allowing external modifications to affect frozen values.

After the fix (adding tuple to the isinstance check), all nested mutable elements
within tuples are properly frozen to guarantee deep immutability.
"""

import pytest

from crystflux.v1 import StrictValue, LatentValue, DreamValue


def test_tuple_mutable_element_list_cannot_be_modified():
    """Attempting to modify a list inside a frozen tuple raises AttributeError.

    Regression test for the bug where tuple contents were not recursively frozen.
    """
    v = StrictValue(([1, 2],))
    frozen = v.as_json_value()
    assert frozen == ((1, 2),)
    with pytest.raises(AttributeError, match="'tuple' object has no attribute 'append'"):
        frozen[0].append(3)  # type: ignore[arg-type]


def test_tuple_mutable_element_dict_cannot_be_modified():
    """Attempting to modify a dict inside a frozen tuple raises TypeError.

    Regression test for the bug where tuple contents were not recursively frozen.
    """
    v = StrictValue(({"key": "value"},))
    frozen = v.as_json_value()
    assert frozen == ({"key": "value"},)
    with pytest.raises(TypeError, match="'mappingproxy' object does not support item assignment"):
        frozen[0]["key"] = "hacked"  # type: ignore[index]


def test_filter_preserves_immutability():
    """Values created via filter() have properly frozen tuple elements.

    Regression test for the bug where filtered values retained references to
    mutable tuple elements from the source.
    """
    inner1, inner2 = [1, 2], [3, 4]
    v = StrictValue((inner1, inner2))
    filtered = v.filter(lambda x: x.at(0).as_int() == 1)

    # External modification should not affect the frozen value
    inner1.append(99)
    assert filtered.as_json_value() == ((1, 2),)

    # Direct modification of the frozen value should fail
    with pytest.raises(AttributeError):
        filtered.as_json_value()[0].append(3)  # type: ignore[arg-type]


def test_first_rest_preserves_immutability():
    """Values created via first_rest() have properly frozen tuple elements.

    Regression test for the bug where rest tuple elements were not recursively frozen.
    """
    v = StrictValue(([1, 2], [3, 4]))
    _, rest = v.first_rest()
    assert rest.as_json_value() == ((3, 4),)
    with pytest.raises(AttributeError):
        rest.as_json_value()[0].append(5)  # type: ignore[arg-type]


def test_rest_last_preserves_immutability():
    """Values created via rest_last() have properly frozen tuple elements.

    Regression test for the bug where rest tuple elements were not recursively frozen.
    """
    v = StrictValue(([1, 2], [3, 4]))
    rest, _ = v.rest_last()
    assert rest.as_json_value() == ((1, 2),)
    with pytest.raises(AttributeError):
        rest.as_json_value()[0].append(5)  # type: ignore[arg-type]


def test_nested_tuple_preserves_immutability():
    """Deeply nested tuple structures are properly frozen.

    Regression test for the bug where nested tuple elements were not recursively frozen.
    """
    v = StrictValue((([1, 2],),))
    frozen = v.as_json_value()
    with pytest.raises(AttributeError):
        frozen[0][0].append(3)  # type: ignore[arg-type]


def test_all_modes_preserve_immutability():
    """All modes (Strict/Latent/Dream) properly freeze tuple contents.

    Regression test to ensure the fix applies across all mode implementations.
    """
    for Mode in [StrictValue, LatentValue, DreamValue]:
        v = Mode(([1, 2],))
        frozen = v.as_json_value()
        assert frozen == ((1, 2),)
        with pytest.raises(AttributeError):
            frozen[0].append(3)  # type: ignore[arg-type]
