# RESUME-03: Junction telemetry unit tests
"""Tests for junction telemetry logging at REST-to-WS handoff."""

from __future__ import annotations

import json
from pathlib import Path
from unittest.mock import MagicMock, patch


class _MockEngine:
    """Minimal mock of StreamManager with get_metrics()."""

    def __init__(self, trades_received: int = 500, dropped_bars: int = 0) -> None:
        self._trades_received = trades_received
        self._dropped_bars = dropped_bars

    def get_metrics(self) -> dict:
        return {
            "trades_received": self._trades_received,
            "dropped_bars": self._dropped_bars,
            "reconnections": 0,
        }


class TestJunctionTelemetry:
    """Tests for _log_junction_telemetry function."""

    def test_computes_ws_buffer_depth_from_engine_metrics(self):
        """Test 1: ws_buffer_depth comes from engine.get_metrics() trades_received."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        engine = _MockEngine(trades_received=1234)

        with patch("opendeviationbar.sidecar.junction_telemetry._junction_state", {
            "BTCUSDT": {"rest_first_tid": 100, "rest_last_tid": 500},
        }):
            _log_junction_telemetry(
                engine=engine,
                aion_symbols=["BTCUSDT"],
                resume_tids={"BTCUSDT": 100},
            )

            # Check _junction_state was enriched
            from opendeviationbar.sidecar.junction_telemetry import _junction_state

            assert _junction_state["BTCUSDT"]["ws_buffer_depth"] == 1234

    def test_no_rest_data_marks_symbol(self):
        """Test 3: When no REST bars exist for a symbol, junction is marked no_rest_data."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        engine = _MockEngine()

        with patch("opendeviationbar.sidecar.junction_telemetry._junction_state", {}):
            # ETHUSDT has no entry in _junction_state => no REST data
            _log_junction_telemetry(
                engine=engine,
                aion_symbols=["ETHUSDT"],
                resume_tids={"ETHUSDT": 100},
            )

            from opendeviationbar.sidecar.junction_telemetry import _junction_state

            assert _junction_state["ETHUSDT"]["status"] == "no_rest_data"

    def test_junction_state_enriched_with_telemetry_fields(self):
        """Test 4: _junction_state is enriched with telemetry fields."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        engine = _MockEngine(trades_received=42)

        with patch("opendeviationbar.sidecar.junction_telemetry._junction_state", {
            "BTCUSDT": {"rest_first_tid": 100, "rest_last_tid": 500},
        }):
            _log_junction_telemetry(
                engine=engine,
                aion_symbols=["BTCUSDT"],
                resume_tids={"BTCUSDT": 50},
            )

            from opendeviationbar.sidecar.junction_telemetry import _junction_state

            sym_data = _junction_state["BTCUSDT"]
            assert "junction_logged_at" in sym_data
            assert sym_data["ws_buffer_depth"] == 42
            assert sym_data["parquet_coverage"] is True  # rest_last_tid(500) > resume_tid(50)

    def test_telemetry_written_as_ndjson(self, tmp_path: Path):
        """Test 5: Telemetry is written as NDJSON to local telemetry log."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        engine = _MockEngine(trades_received=99)
        ndjson_path = tmp_path / "events.jsonl"

        with (
            patch("opendeviationbar.sidecar.junction_telemetry._junction_state", {
                "BTCUSDT": {"rest_first_tid": 100, "rest_last_tid": 500},
            }),
            patch(
                "opendeviationbar.sidecar.junction_telemetry.NDJSON_FILE",
                ndjson_path,
            ),
        ):
            _log_junction_telemetry(
                engine=engine,
                aion_symbols=["BTCUSDT"],
                resume_tids={"BTCUSDT": 100},
            )

        assert ndjson_path.exists()
        lines = ndjson_path.read_text().strip().split("\n")
        assert len(lines) >= 1
        event = json.loads(lines[0])
        assert event["event"] == "junction_telemetry"
        assert event["symbol"] == "BTCUSDT"
        assert event["rest_last_tid"] == 500
        assert event["ws_buffer_depth"] == 99

    def test_non_fatal_on_exception(self):
        """Test: Entire function is non-fatal (exception does not propagate)."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        # engine.get_metrics() raises => function should catch and warn
        engine = MagicMock()
        engine.get_metrics.side_effect = RuntimeError("engine dead")

        # Should not raise
        _log_junction_telemetry(
            engine=engine,
            aion_symbols=["BTCUSDT"],
            resume_tids={"BTCUSDT": 100},
        )

    def test_parquet_coverage_false_when_no_advancement(self):
        """Test: parquet_coverage is False when rest_last_tid == resume_tid."""
        from opendeviationbar.sidecar.junction_telemetry import _log_junction_telemetry

        engine = _MockEngine()

        with patch("opendeviationbar.sidecar.junction_telemetry._junction_state", {
            "BTCUSDT": {"rest_first_tid": 100, "rest_last_tid": 100},
        }):
            _log_junction_telemetry(
                engine=engine,
                aion_symbols=["BTCUSDT"],
                resume_tids={"BTCUSDT": 100},
            )

            from opendeviationbar.sidecar.junction_telemetry import _junction_state

            assert _junction_state["BTCUSDT"]["parquet_coverage"] is False
