"""Tests for DefaultVoidSentinel adapter."""

import pytest

from crystflux.v1.adapters.default_void_sentinel import DefaultVoidSentinel
from crystflux.v1.core.void_reason import VoidReason


class TestDefaultVoidSentinel:
    """Test case for DefaultVoidSentinel adapter."""

    def test_initialization(self) -> None:
        """Test DefaultVoidSentinel initialization."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="dream", void_reasoning=True
        )

        assert sentinel.reason == VoidReason.MISSING_KEY
        assert sentinel.mode == "dream"
        assert sentinel.void_reasoning is True
        assert sentinel.count == 1
        assert sentinel.chain_steps == ()

    def test_initialization_with_string_reason(self) -> None:
        """Test initialization with string reason."""
        sentinel = DefaultVoidSentinel(reason="custom_reason", mode="latent", void_reasoning=False)

        assert sentinel.reason == "custom_reason"
        assert sentinel.mode == "latent"
        assert sentinel.void_reasoning is False

    def test_bump_increments_count_and_appends_step(self) -> None:
        """Test bump() increments count and appends chain step."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="dream", void_reasoning=True
        )

        # Bump with step
        bumped = sentinel.bump(step="test_op")
        assert bumped.count == 2
        assert len(bumped.chain_steps) == 1
        assert bumped.chain_steps[0].tag == "bump"
        assert bumped.chain_steps[0].op == "test_op"

        # Multiple bumps
        bumped_twice = bumped.bump(step="second_op")
        assert bumped_twice.count == 3
        assert len(bumped_twice.chain_steps) == 2
        assert bumped_twice.chain_steps[0].op == "test_op"
        assert bumped_twice.chain_steps[1].op == "second_op"

    def test_bump_without_step(self) -> None:
        """Test bump() without step parameter."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.DESIRE_CALL, mode="latent", void_reasoning=False
        )

        # Bump without step (step=None)
        bumped = sentinel.bump()
        assert bumped.count == 2
        assert len(bumped.chain_steps) == 0

    def test_bump_with_existing_chain_steps(self) -> None:
        """Test bump() with existing chain_steps."""
        from crystflux.v1.boundary.chain_step import ChainStep

        initial_steps = (ChainStep(tag="spawn", op="get"),)
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.TYPE_MISMATCH,
            mode="dream",
            void_reasoning=True,
            chain_steps=initial_steps,
        )

        bumped = sentinel.bump(step="at")
        assert len(bumped.chain_steps) == 2
        assert bumped.chain_steps[0].tag == "spawn"
        assert bumped.chain_steps[0].op == "get"
        assert bumped.chain_steps[1].tag == "bump"
        assert bumped.chain_steps[1].op == "at"

    def test_to_cryst_dream_mode(self) -> None:
        """Test to_cryst() with dream mode."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.DESIRE_CALL, mode="dream", void_reasoning=True
        )

        result = sentinel.to_cryst()
        assert result.is_void()
        assert type(result).__name__ == "DreamValue"

    def test_to_cryst_latent_mode(self) -> None:
        """Test to_cryst() with latent mode."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="latent", void_reasoning=True
        )

        result = sentinel.to_cryst()
        assert result.is_void()

    def test_to_cryst_with_mode_override(self) -> None:
        """Test to_cryst() with mode override."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="latent", void_reasoning=True
        )

        # Override to dream mode
        result = sentinel.to_cryst(mode_override="dream")
        assert result.is_void()
        assert type(result).__name__ == "DreamValue"

        # Override to latent mode (same as original)
        result_latent = sentinel.to_cryst(mode_override="latent")
        assert result_latent.is_void()

    def test_to_cryst_strict_raises(self) -> None:
        """Test to_cryst() with strict mode raises TypeError."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.TYPE_MISMATCH, mode="strict", void_reasoning=False
        )

        with pytest.raises(TypeError, match="Void sentinel encountered in strict mode"):
            sentinel.to_cryst()

    def test_to_cryst_with_string_reason(self) -> None:
        """Test to_cryst() with string reason."""
        sentinel = DefaultVoidSentinel(reason="custom_error", mode="dream", void_reasoning=True)

        result = sentinel.to_cryst()
        assert result.is_void()

    def test_to_cryst_without_void_reasoning(self) -> None:
        """Test to_cryst() with void_reasoning=False."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="dream", void_reasoning=False
        )

        result = sentinel.to_cryst()
        assert result.is_void()

    def test_to_cryst_latent_without_void_reasoning(self) -> None:
        """Test to_cryst() with latent mode and void_reasoning=False."""
        sentinel = DefaultVoidSentinel(
            reason=VoidReason.MISSING_KEY, mode="latent", void_reasoning=False
        )

        result = sentinel.to_cryst()
        assert result.is_void()
        from crystflux.v1.core import VOID

        assert result is VOID

    def test_to_cryst_latent_with_string_reason(self) -> None:
        """Test to_cryst() with latent mode and string reason."""
        sentinel = DefaultVoidSentinel(reason="custom_error", mode="latent", void_reasoning=True)

        result = sentinel.to_cryst()
        assert result.is_void()
        from crystflux.v1.core import VOID

        assert result is VOID
