from __future__ import annotations

from crystflux.v1 import Crystallizer
from crystflux.v1.adapters import enable_dream_mode
from crystflux.v1.boundary.event_schema import MissingEvent
from crystflux.v1.core.void_reason import VoidReason


class ListMissingLogger:
    def __init__(self) -> None:
        self.events: list[MissingEvent] = []

    def log(self, event: MissingEvent) -> None:
        self.events.append(event)


def test_latent_missing_key_emits_missing_event() -> None:
    logger = ListMissingLogger()
    value = Crystallizer.latent({"a": 1}, missing_logger=logger)
    voided = value.get("missing")
    assert voided.is_void()

    assert len(logger.events) == 1
    event = logger.events[0]
    assert event.reason is VoidReason.MISSING_KEY
    assert event.key == "missing"
    assert event.index is None
    assert event.target_type == "mappingproxy"
    assert event.target_repr
    assert event.source_location is not None
    assert event.timestamp is not None


def test_latent_missing_index_emits_missing_event() -> None:
    logger = ListMissingLogger()
    value = Crystallizer.latent([1, 2], missing_logger=logger)
    voided = value.at(99)
    assert voided.is_void()

    assert len(logger.events) == 1
    event = logger.events[0]
    assert event.reason is VoidReason.MISSING_INDEX
    assert event.key is None
    assert event.index == 99
    assert event.target_type == "tuple"
    assert event.target_repr
    assert event.source_location is not None
    assert event.timestamp is not None


def test_dream_missing_key_emits_missing_event() -> None:
    logger = ListMissingLogger()
    with enable_dream_mode():
        value = Crystallizer.dream({"a": 1}, missing_logger=logger)
        voided = value.get("missing")
    assert voided.is_void()

    assert len(logger.events) == 1
    event = logger.events[0]
    assert event.reason is VoidReason.MISSING_KEY
    assert event.key == "missing"


def test_missing_logger_exception_does_not_break_chain() -> None:
    class FailingLogger:
        def log(self, event: MissingEvent) -> None:
            raise RuntimeError("logger failure")

    value = Crystallizer.latent({"a": 1}, missing_logger=FailingLogger())
    voided = value.get("missing")
    assert voided.is_void()
