"""Tests for the perf-stat collector — parser logic in isolation
(no actual perf invocation, since perf needs CAP_SYS_ADMIN and
hardware counters that the test runner can't assume)."""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from collectors import perf_qemu


def test_parse_event_line_extracts_fields() -> None:
    line = '{"interval":0.100123,"counter-value":"1234567","unit":"","event":"cycles"}'
    evt = perf_qemu.parse_perf_event_line(line)
    assert evt is not None
    assert evt["event"] == "cycles"
    assert evt["interval"] == 0.100123
    assert evt["counter-value"] == "1234567"


def test_parse_event_line_skips_non_json() -> None:
    assert perf_qemu.parse_perf_event_line("") is None
    assert perf_qemu.parse_perf_event_line("garbage") is None
    assert perf_qemu.parse_perf_event_line("# Performance counter stats") is None


def test_coerce_int_handles_perf_quirks() -> None:
    assert perf_qemu._coerce_int("1234567") == 1234567
    assert perf_qemu._coerce_int("1,234,567") == 1234567
    assert perf_qemu._coerce_int("<not counted>") is None
    assert perf_qemu._coerce_int("<not supported>") is None
    assert perf_qemu._coerce_int("") is None
    assert perf_qemu._coerce_int(None) is None
    assert perf_qemu._coerce_int(42) == 42


def test_build_row_computes_ipc_and_miss_rate() -> None:
    agg = {
        "cycles": 1_000_000_000,
        "instructions": 660_000_000,
        "cache-references": 1_000_000,
        "cache-misses": 50_000,
        "branches": 100_000_000,
        "branch-misses": 5_000_000,
        "page-faults": 12,
        "context-switches": 20,
    }
    row = perf_qemu._build_row(t_mono_origin_ns=0, interval_s=0.1, agg=agg)
    assert row["source"] == "host_perf"
    assert row["available_in_deployment"] is False
    assert row["cycles"] == 1_000_000_000
    assert row["instructions"] == 660_000_000
    assert pytest.approx(row["ipc"], abs=1e-9) == 0.66
    assert pytest.approx(row["cache_miss_rate"], abs=1e-9) == 0.05
    assert row["interval_s"] == 0.1


def test_build_row_handles_missing_counters() -> None:
    """If perf can't enable cache-misses on this hardware, the row
    should still be valid — just with None for the missing fields."""
    agg = {"cycles": 100, "instructions": 50}
    row = perf_qemu._build_row(t_mono_origin_ns=0, interval_s=0.1, agg=agg)
    assert row["cycles"] == 100
    assert row["cache_misses"] is None
    assert row["cache_miss_rate"] is None
    assert pytest.approx(row["ipc"], abs=1e-9) == 0.5


def test_run_loop_returns_zero_when_perf_missing(tmp_path: Path, monkeypatch) -> None:
    monkeypatch.setattr(perf_qemu, "perf_available", lambda: False)
    import threading
    rows = perf_qemu.run_loop(
        pid=1,
        output_path=tmp_path / "telemetry-perf.jsonl",
        t_mono_origin_ns=0,
        interval_ms=100,
        stop_event=threading.Event(),
    )
    assert rows == 0
