"""Tests for BaseChainVesicle and EmptyChainContext."""

import pytest

from crystflux.v1.adapters.base_chain_vesicle import BaseChainVesicle, EmptyChainContext
from crystflux.v1.adapters.default_void_sentinel import DefaultVoidSentinel
from crystflux.v1.boundary.chain_context import ChainContextLayer
from crystflux.v1.boundary.chain_step import ChainStep
from crystflux.v1.core.void_reason import VoidReason

# Test constants
VOID_REASON = VoidReason.MISSING_KEY
VOID_REASONING = True
LAYER_1 = ChainContextLayer(tag="layer1", payload={"key": "value1"})
LAYER_2 = ChainContextLayer(tag="layer2", payload={"key": "value2"})
SPAWN_STEP = ChainStep(tag="spawn", op="get")
BUMP_STEP = ChainStep(tag="bump", op="filter")
SENTINEL_STRICT = DefaultVoidSentinel(
    reason=VOID_REASON, mode="strict", void_reasoning=VOID_REASONING
)
SENTINEL_DREAM = DefaultVoidSentinel(
    reason=VoidReason.DESIRE_CALL, mode="dream", void_reasoning=VOID_REASONING
)


class TestBaseChainVesicle:
    """Test case for BaseChainVesicle."""

    # Group 1: プロパティテスト
    def test_is_void_false_when_no_sentinel(self) -> None:
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=None)
        assert vesicle.is_void is False

    def test_is_void_true_when_sentinel_attached(self) -> None:
        vesicle = BaseChainVesicle(
            mode="strict", context=EmptyChainContext(), sentinel=SENTINEL_STRICT
        )
        assert vesicle.is_void is True

    # Group 2: chain_steps プロパティ
    def test_chain_steps_returns_local_steps_before_void(self) -> None:
        vesicle = BaseChainVesicle(
            mode="strict", context=EmptyChainContext(), sentinel=None, _steps=(SPAWN_STEP,)
        )
        assert vesicle.chain_steps == (SPAWN_STEP,)

    def test_chain_steps_returns_sentinel_steps_after_void(self) -> None:
        sentinel = DefaultVoidSentinel(
            reason=VOID_REASON,
            mode="strict",
            void_reasoning=VOID_REASONING,
            chain_steps=(BUMP_STEP,),
        )
        vesicle = BaseChainVesicle(
            mode="strict", context=EmptyChainContext(), sentinel=sentinel, _steps=(SPAWN_STEP,)
        )
        assert vesicle.chain_steps == (BUMP_STEP,)

    # Group 3: 基本操作メソッド
    def test_coat_adds_layer_and_returns_new_instance(self) -> None:
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=None)
        coated = vesicle.coat(LAYER_1)
        assert coated.context.layers == (LAYER_1,)
        assert coated is not vesicle

    def test_imprint_adds_spawn_step_before_void(self) -> None:
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=None)
        imprinted = vesicle.imprint("get")
        assert imprinted.chain_steps == (ChainStep(tag="spawn", op="get"),)
        assert imprinted is not vesicle

    def test_imprint_bumps_sentinel_after_void(self) -> None:
        sentinel = DefaultVoidSentinel(
            reason=VOID_REASON,
            mode="strict",
            void_reasoning=VOID_REASONING,
            chain_steps=(),
        )
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=sentinel)
        imprinted = vesicle.imprint("filter")
        assert imprinted.sentinel is not None
        assert imprinted.sentinel.count == 2
        assert imprinted.sentinel.chain_steps == (ChainStep(tag="bump", op="filter"),)
        assert imprinted is not vesicle

    def test_seal_attaches_sentinel(self) -> None:
        vesicle = BaseChainVesicle(mode="dream", context=EmptyChainContext(), sentinel=None)
        sealed = vesicle.seal(SENTINEL_DREAM)
        assert sealed.sentinel == SENTINEL_DREAM
        assert sealed.is_void is True
        assert sealed is not vesicle

    def test_seal_raises_on_mode_mismatch(self) -> None:
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=None)
        with pytest.raises(ValueError, match="Mode mismatch between vesicle and sentinel"):
            vesicle.seal(SENTINEL_DREAM)

    # Group 4: プロジェクションメソッド
    def test_project_ops_returns_ops_from_chain_steps(self) -> None:
        vesicle = BaseChainVesicle(
            mode="strict",
            context=EmptyChainContext(),
            sentinel=None,
            _steps=(
                ChainStep(tag="spawn", op="get"),
                ChainStep(tag="spawn", op="filter"),
            ),
        )
        assert vesicle.project_ops() == ("get", "filter")

    def test_project_context_tags_returns_tags_from_layers(self) -> None:
        context = EmptyChainContext(layers=(LAYER_1, LAYER_2))
        vesicle = BaseChainVesicle(mode="strict", context=context, sentinel=None)
        assert vesicle.project_context_tags() == ("layer1", "layer2")

    # Group 5: サマリーメソッド
    def test_void_summary_none_before_void(self) -> None:
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=None)
        assert vesicle.void_summary() is None

    def test_void_summary_returns_summary_after_void(self) -> None:
        sentinel = DefaultVoidSentinel(
            reason=VOID_REASON,
            mode="strict",
            void_reasoning=VOID_REASONING,
            count=1,
            chain_steps=(),
        )
        vesicle = BaseChainVesicle(mode="strict", context=EmptyChainContext(), sentinel=sentinel)
        summary = vesicle.void_summary()
        assert summary is not None
        assert summary.mode == "strict"
        assert summary.reason == VOID_REASON
        assert summary.count == 1
        assert summary.void_reasoning is True
        assert summary.chain_steps == ()


class TestEmptyChainContext:
    """Test case for EmptyChainContext."""

    def test_evolve_adds_layer_and_returns_new_instance(self) -> None:
        context = EmptyChainContext()
        evolved = context.evolve(LAYER_1)
        assert evolved.layers == (LAYER_1,)
        assert evolved is not context

        evolved_twice = evolved.evolve(LAYER_2)
        assert evolved_twice.layers == (LAYER_1, LAYER_2)
        assert evolved_twice is not evolved
