from __future__ import annotations

from crystflux.v1 import Crystallizer
from crystflux.v1.adapters import enable_dream_mode
from crystflux.v1.core.value_array import LatentArrayValue
from crystflux.v1.core.value_modes import DreamValue, StrictValue
from crystflux.v1.core.void_value import ReasonedVoid
from crystflux.v1.core.void_reason import VoidReason


def test_with_value_map_latent_doubles() -> None:
    # JP: with_valueで値を差し替え、Latentのままmap結果がtupleになることを確認する
    # EN: with_value replaces values while preserving Latent mode; map returns a tuple result
    value = Crystallizer.latent([1, 2, 3, 4, 5])
    doubled = value.map(lambda x: x.with_value(x.expected_int() * 2))
    assert doubled.as_json_value() == (2, 4, 6, 8, 10)


def test_with_value_filter_latent_evens() -> None:
    # JP: filterで偶数だけ残し、Latentのままtupleとして返ることを確認する
    # EN: filter keeps even values and returns a tuple in Latent mode
    value = Crystallizer.latent([1, 2, 3, 4])
    evens = value.filter(lambda x: (x.as_int() or 0) % 2 == 0)
    assert evens.as_json_value() == (2, 4)


def test_with_value_no_revive_latent_void_chain() -> None:
    # JP: VOIDはwith_valueで復帰せずNoneに正規化されることを確認する
    # EN: VOID does not revive via with_value and normalizes to None in the result
    value = Crystallizer.latent([{"a": 1}, {}])
    mapped = value.map(lambda x: x.get("missing").with_value(1))
    assert mapped.as_json_value() == (None, None)


def test_with_value_no_revive_dream_void_chain() -> None:
    # JP: DreamでもVOIDはwith_valueで復帰しないことを確認する
    # EN: VOID still does not revive via with_value in Dream mode
    with enable_dream_mode():
        value = Crystallizer.dream([{"a": 1}, {}])
        mapped = value.map(lambda x: x.get("missing").with_value(1))
    assert mapped.as_json_value() == (None, None)


def test_with_value_map_mixed_voids() -> None:
    # JP: 欠損が混ざるとVOIDはNoneになり、非VOIDは置換されることを確認する
    # EN: Mixed missing values turn into None while non-VOID entries are replaced
    value = Crystallizer.latent([{"a": 1}, {"b": 2}, {"a": 3}])
    mapped = value.map(lambda x: x.get("a").with_value(0))
    assert mapped.as_json_value() == (0, None, 0)


def test_with_value_latent_void_reasoning_keeps_reasoned_void() -> None:
    # JP: void_reasoning有効時、ReasonedVoidはwith_valueでも理由を保持する
    # EN: With void_reasoning enabled, ReasonedVoid keeps its reason after with_value
    value = Crystallizer.latent({"a": 1}, void_reasoning=True)
    voided = value.get("missing")
    revived = voided.with_value(1)
    assert isinstance(revived, ReasonedVoid)
    assert revived.void_reason() is VoidReason.MISSING_KEY


def test_with_value_preserves_void_reasoning_flags() -> None:
    # JP: with_valueはvoid_reasoningフラグを継承することを確認する
    # EN: with_value preserves the void_reasoning flag
    latent = Crystallizer.latent({"a": 1}, void_reasoning=True)
    with enable_dream_mode():
        dream = Crystallizer.dream({"a": 1}, void_reasoning=True)
    assert latent.with_value({"b": 2}).is_void_reasoning_enabled()
    assert dream.with_value({"b": 2}).is_void_reasoning_enabled()


def test_with_value_filter_then_map_keeps_non_voids() -> None:
    # JP: filterで欠損を落とした後、with_valueで置換できることを確認する
    # EN: filter removes missing entries, then with_value replaces remaining values
    value = Crystallizer.latent([{"a": 1}, {"b": 2}, {"a": 3}])
    filtered = value.filter(lambda x: x.get("a").has_value())
    mapped = filtered.map(lambda x: x.get("a").with_value(0))
    assert mapped.as_json_value() == (0, 0)


def test_with_value_filter_then_map_expected_ints() -> None:
    # JP: 型ガード後にexpected_intで安全に倍化できることを確認する
    # EN: After type guarding, expected_int allows safe doubling
    value = Crystallizer.latent([1, "x", 3])
    filtered = value.filter(lambda x: x.as_int() is not None)
    mapped = filtered.map(lambda x: x.with_value(x.expected_int() * 2))
    assert mapped.as_json_value() == (2, 6)


def test_with_value_dream_mixed_voids() -> None:
    # JP: Dreamで欠損が混ざるとNoneになり、非VOIDは置換されることを確認する
    # EN: In Dream mode, missing entries become None while non-VOID values are replaced
    with enable_dream_mode():
        value = Crystallizer.dream([{"a": 1}, {}])
        mapped = value.map(lambda x: x.get("a").with_value(9))
    assert mapped.as_json_value() == (9, None)


def test_with_value_zero_fallback_for_mixed_types() -> None:
    # JP: 混在型を0フォールバックして計算を継続できることを確認する
    # EN: Mixed types fall back to 0 so computation continues
    value = Crystallizer.latent([1, "x", 3])
    mapped = value.map(lambda x: x.with_value((x.as_int() or 0) * 2))
    assert mapped.as_json_value() == (2, 0, 6)


def test_with_value_strict_preserves_mode() -> None:
    # JP: Strictでwith_valueしてもStrictのまま返ることを確認する
    # EN: with_value keeps Strict mode intact
    strict = Crystallizer.strict([1, 2, 3])
    replaced = strict.with_value([4, 5])
    assert isinstance(replaced, StrictValue)
    assert replaced.as_json_value() == (4, 5)


def test_with_value_array_value_preserves_array_mode() -> None:
    # JP: ArrayValueでwith_valueしてもArrayValueのまま返ることを確認する
    # EN: with_value preserves ArrayValue type
    arr_value = Crystallizer.latent([1, 2]).to_array()
    replaced = arr_value.with_value((3, 4))
    assert isinstance(replaced, LatentArrayValue)
    assert replaced.as_json_value() == (3, 4)


def test_with_value_nested_map_over_arrays() -> None:
    # JP: ネストしたmapでも内外でwith_valueが機能することを確認する
    # EN: with_value works in nested map chains (outer and inner arrays)
    value = Crystallizer.latent([[1, 2], [3, 4]])
    mapped = value.map(lambda row: row.to_array().map(lambda x: x.with_value(x.expected_int() + 1)))
    assert mapped.as_json_value() == ((2, 3), (4, 5))


def test_with_value_dream_chain_steps_preserved() -> None:
    # JP: Dreamのchain_stepsがwith_value後も保持されることを確認する
    # EN: Dream chain_steps are preserved after with_value
    with enable_dream_mode():
        dream = Crystallizer.dream({"a": 1}, void_reasoning=True)
        voided = dream.nonexistent()
        replaced = voided.with_value(1)
    assert isinstance(replaced, DreamValue)
    assert getattr(replaced, "chain_steps") == ("nonexistent",)


def test_with_value_non_array_map_returns_reasoned_void() -> None:
    # JP: 非配列にmapするとReasonedVoid（NON_ARRAY_OPERATION）になることを確認する
    # EN: map on a non-array returns a ReasonedVoid with NON_ARRAY_OPERATION
    value = Crystallizer.latent({"a": 1}, void_reasoning=True)
    mapped = value.map(lambda x: x.with_value(1))
    assert mapped.is_void()
    assert mapped.void_reason() is VoidReason.NON_ARRAY_OPERATION
