"""Tests for LatentValue mode behavior."""
from typing import Any

import pytest

from crystflux.v1 import Crystallizer, LatentValue, VOID
from crystflux.v1.core.json_types import JsonValue
from crystflux.v1.core.void_reason import VoidReason
from crystflux.v1.core.value_array import LatentArrayValue


def test_latent_void_fallback_and_accessors(tao_data: dict[str, Any]) -> None:
    """Test latent mode void fallback and accessors."""
    laozi = LatentValue(tao_data["laozi"])
    assert laozi.get("author").as_str() == "老子"
    assert laozi.get("missing").is_void()
    assert laozi.get("works").at(10).is_void()
    assert laozi.get("era").as_int() is None
    with pytest.raises(TypeError):
        laozi.get("era").expected_int()

    latent_scalar = LatentValue(123)
    assert latent_scalar.get("x").is_void()
    assert latent_scalar.at(0).is_void()


def test_as_str_array_and_expected(tao_data: dict[str, Any]) -> None:
    """Test as_str_array and expected_str_array for latent mode."""
    works = tao_data["laozi"]["works"]

    latent_laozi = Crystallizer.latent(tao_data["laozi"])
    assert latent_laozi.get("works").as_str_array() == tuple(works)
    assert latent_laozi.get("works").expected_str_array() == tuple(works)

    anthology = Crystallizer.latent(tao_data["anthology"])
    assert anthology.get("fragments").as_str_array() == ()
    with pytest.raises(TypeError):
        anthology.get("fragments").expected_str_array()
    assert latent_laozi.get("author").as_str_array() is None
    with pytest.raises(TypeError):
        latent_laozi.get("author").expected_str_array()
    assert latent_laozi.get("missing").as_str_array() is None
    with pytest.raises(TypeError):
        latent_laozi.get("missing").expected_str_array()


def test_as_int_array_and_expected() -> None:
    """Test as_int_array and expected_int_array for latent mode."""
    latent = LatentValue({"nums": [1, "x", 3]})
    assert latent.get("nums").as_int_array() == (1, 3)
    with pytest.raises(TypeError):
        latent.get("nums").expected_int_array()

    non_array = LatentValue({"x": 1})
    assert non_array.as_int_array() is None
    with pytest.raises(TypeError):
        non_array.expected_int_array()

    void_chain = LatentValue({"a": 1}).get("missing")
    assert void_chain.as_int_array() is None
    with pytest.raises(TypeError):
        void_chain.expected_int_array()


def test_as_float_array_and_expected() -> None:
    """Test as_float_array and expected_float_array for latent mode."""
    latent = LatentValue({"nums": [1.1, "x", 2.2]})
    assert latent.get("nums").as_float_array() == (1.1, 2.2)
    with pytest.raises(TypeError):
        latent.get("nums").expected_float_array()

    non_array = LatentValue({"x": 1.1})
    assert non_array.as_float_array() is None
    with pytest.raises(TypeError):
        non_array.expected_float_array()

    void_chain = LatentValue({"a": 1.1}).get("missing")
    assert void_chain.as_float_array() is None
    with pytest.raises(TypeError):
        void_chain.expected_float_array()


def test_as_scalars_collects_nested_scalars() -> None:
    """Test as_scalars for latent mode."""
    non_array = LatentValue({"x": 1})
    assert non_array.as_scalars() is None

    void_chain = LatentValue({"a": 1}).get("missing")
    assert void_chain.as_scalars() is None


def test_handle_non_array_split_modes() -> None:
    """Test that latent mode returns VOID for split operations on non-arrays."""
    latent_non_array = LatentValue({"key": "value"})
    assert latent_non_array.first_rest() == (VOID, VOID)
    assert latent_non_array.rest_last() == (VOID, VOID)


def test_array_operations_type_errors_and_void_fallbacks() -> None:
    """Test array operations with void fallbacks for latent mode."""
    latent_non_array = LatentArrayValue({"key": "value"})

    map_result = latent_non_array.map(lambda v: v)
    assert map_result.is_void()

    filter_result = latent_non_array.filter(lambda v: True)
    assert filter_result.is_void()


def test_internal_array_helper_methods() -> None:
    """Test internal array helper methods for latent mode."""
    latent_array = LatentArrayValue([1, 2, 3])
    latent_non_array = LatentArrayValue({"key": "value"})

    latent_result = latent_array._require_array_or_void()  # type: ignore[reportPrivateUsage]
    assert latent_result == (1, 2, 3)

    latent_split_result = latent_non_array._handle_non_array_split()  # type: ignore[reportPrivateUsage]
    assert latent_split_result == (VOID, VOID)


def test_array_split_operations_direct_coverage() -> None:
    """Test array split operations directly for latent mode."""
    latent_non_array = LatentArrayValue({"key": "value"})

    latent_first_rest = latent_non_array.first_rest()
    assert latent_first_rest == (VOID, VOID)

    latent_rest_last = latent_non_array.rest_last()
    assert latent_rest_last == (VOID, VOID)


def test_on_missing_hook_exception_does_not_break_chain() -> None:
    """Missing hook errors must not break Latent chains."""

    def raising_hook(
        *,
        reason: VoidReason,
        target: JsonValue,
        key: str | None = None,
        index: int | None = None,
    ) -> None:
        raise RuntimeError("boom")

    latent = LatentValue({"a": 1}, on_missing=raising_hook)
    voided = latent.get("missing")
    assert voided is VOID
