import concurrent.futures
import sys
from dataclasses import dataclass
from typing import Any

import aitop.data_collector as data_collector_module
from aitop.core.gpu.base import GPUInfo
from aitop.data_collector import DataCollector


class _FakeAIMonitor:
    def get_ai_processes(
        self, gpu_processes: list[dict[str, Any]], force_refresh: bool
    ) -> list[dict[str, Any]]:
        del force_refresh
        return [{"pid": 7, "source": "ai_monitor", "gpu_processes": gpu_processes}]


class _FakeMemoryMonitor:
    def get_memory_stats(self) -> dict[str, Any]:
        return {"percent": 42.0}

    def get_memory_by_type(self) -> dict[str, float]:
        return {"cached": 128.0}


class _FakeFuture:
    def __init__(self, value: Any = None, error: Exception | None = None) -> None:
        self._value = value
        self._error = error

    def result(self, timeout: float) -> Any:
        del timeout
        if self._error is not None:
            raise self._error
        return self._value

    def cancel(self) -> bool:
        return True


class _CancelableTimeoutFuture(_FakeFuture):
    def __init__(self) -> None:
        super().__init__(error=concurrent.futures.TimeoutError())
        self.cancel_calls = 0

    def cancel(self) -> bool:
        self.cancel_calls += 1
        return True


class _FakeExecutor:
    def __init__(
        self,
        mapping: dict[str, _FakeFuture],
        submit_errors: dict[str, Exception] | None = None,
    ) -> None:
        self._mapping = mapping
        self._submit_errors = submit_errors or {}
        self.submitted: list[str] = []
        self.shutdown_calls: list[tuple[bool, bool]] = []

    def submit(self, fn: Any, *args: Any, **kwargs: Any) -> _FakeFuture:
        del args, kwargs
        name = getattr(fn, "__name__", "unknown")
        self.submitted.append(name)
        if name in self._submit_errors:
            raise self._submit_errors[name]
        return self._mapping.get(name, _FakeFuture())

    def shutdown(self, wait: bool = False, cancel_futures: bool = False) -> None:
        self.shutdown_calls.append((wait, cancel_futures))

    def __enter__(self) -> "_FakeExecutor":
        return self

    def __exit__(self, exc_type, exc, tb) -> bool:
        return False


@dataclass
class _FakeGPU:
    index: int
    utilization: float = 0.0
    memory_used: float = 0.0
    memory_total: float = 0.0


class _FakeThread:
    def __init__(self, alive_states: list[bool]) -> None:
        self._alive_states = alive_states
        self.join_calls: list[float] = []

    def is_alive(self) -> bool:
        if len(self._alive_states) > 1:
            return self._alive_states.pop(0)
        return self._alive_states[0]

    def join(self, timeout: float) -> None:
        self.join_calls.append(timeout)


def _make_collector(monkeypatch: Any) -> DataCollector:
    monkeypatch.setattr(
        data_collector_module.GPUMonitorFactory,
        "create_monitors",
        lambda: [],
    )
    monkeypatch.setattr(
        data_collector_module.GPUMonitorFactory,
        "detect_vendors",
        lambda: [],
    )
    monkeypatch.setattr(
        data_collector_module.GPUMonitorFactory,
        "detect_dependency_warnings",
        lambda: [],
    )
    monkeypatch.setattr(data_collector_module, "AIProcessMonitor", _FakeAIMonitor)
    monkeypatch.setattr(
        data_collector_module,
        "SystemMemoryMonitor",
        _FakeMemoryMonitor,
    )

    return DataCollector(
        update_interval=0.5,
        process_interval=2.0,
        gpu_info_interval=1.0,
        max_workers=1,
    )


def _install_fake_executor(
    collector: DataCollector,
    mapping: dict[str, _FakeFuture],
    submit_errors: dict[str, Exception] | None = None,
) -> _FakeExecutor:
    collector.executor.shutdown(wait=False, cancel_futures=True)
    fake_executor = _FakeExecutor(mapping, submit_errors=submit_errors)
    collector.executor = fake_executor
    return fake_executor


def test_collect_data_schedules_expected_tasks_and_updates_cache(
    monkeypatch: Any,
) -> None:
    collector = _make_collector(monkeypatch)
    fake_executor = _install_fake_executor(
        collector,
        {
            "get_stats": _FakeFuture(value="cpu-stats"),
            "get_memory_stats": _FakeFuture(value={"percent": 42.0}),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "get_ai_processes": _FakeFuture(value=[{"pid": 7, "name": "python"}]),
        },
    )

    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 0.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=100.0)

    assert success is True
    assert collector._cache["cpu_stats"] == "cpu-stats"
    assert collector._cache["memory_stats"] == {"percent": 42.0}
    assert collector._cache["memory_types"] == {"cached": 128.0}
    assert collector._cache["processes"] == [{"pid": 7, "name": "python"}]
    assert collector._cache["last_process_update"] == 100.0
    assert collector._next_runs["system"] == 100.5
    assert collector._next_runs["processes"] == 102.0
    assert set(fake_executor.submitted) == {
        "get_stats",
        "get_memory_stats",
        "get_memory_by_type",
        "get_ai_processes",
    }


def test_collect_data_marks_cpu_timeout_as_critical(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    _install_fake_executor(
        collector,
        {
            "get_stats": _FakeFuture(error=concurrent.futures.TimeoutError()),
            "get_memory_stats": _FakeFuture(value={"percent": 42.0}),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "get_ai_processes": _FakeFuture(value=[]),
        },
    )

    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 0.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=200.0)

    assert success is False
    assert any(
        err["component"] == "cpu" and err["error"] == "Timeout"
        for err in collector._cache["errors"]
    )


def test_collect_data_marks_gpu_full_error_as_critical_after_threshold(
    monkeypatch: Any,
) -> None:
    collector = _make_collector(monkeypatch)
    _install_fake_executor(
        collector,
        {
            "_collect_full_gpu_info": _FakeFuture(
                error=RuntimeError("gpu full failed")
            ),
        },
    )

    collector.gpu_monitors = [object()]
    collector._next_runs.update(
        {
            "system": 9999.0,
            "gpu_quick": 9999.0,
            "gpu_full": 0.0,
            "processes": 9999.0,
            "error_cleanup": 9999.0,
        }
    )
    collector._cache["errors"] = [
        {"timestamp": 0.0, "component": "seed", "error": "e1"},
        {"timestamp": 0.0, "component": "seed", "error": "e2"},
        {"timestamp": 0.0, "component": "seed", "error": "e3"},
        {"timestamp": 0.0, "component": "seed", "error": "e4"},
    ]

    success = collector._collect_data(current_time=300.0)

    assert success is False
    assert any(
        err["component"] == "gpu_full" and "gpu full failed" in err["error"]
        for err in collector._cache["errors"]
    )


def test_collect_data_marks_process_timeout_non_critical(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)

    timed_out_future = _CancelableTimeoutFuture()
    fake_executor = _install_fake_executor(
        collector,
        {
            "get_stats": _FakeFuture(value="cpu-stats"),
            "get_memory_stats": _FakeFuture(value={"percent": 42.0}),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "get_ai_processes": timed_out_future,
        },
    )

    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 0.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=600.0)

    assert success is True
    assert "get_ai_processes" in fake_executor.submitted
    assert any(
        err["component"] == "processes" and "Timeout" in err["error"]
        for err in collector._cache["errors"]
    )
    assert timed_out_future.cancel_calls == 1


def test_collect_data_memory_timeout_uses_last_known_good_value(
    monkeypatch: Any,
) -> None:
    collector = _make_collector(monkeypatch)
    collector._cache["memory_stats"] = {"percent": 38.0}
    collector._cache["memory_types"] = {"cached": 64.0}
    _install_fake_executor(
        collector,
        {
            "get_stats": _FakeFuture(value="cpu-stats"),
            "get_memory_stats": _FakeFuture(error=concurrent.futures.TimeoutError()),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "get_ai_processes": _FakeFuture(value=[]),
        },
    )

    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 0.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=650.0)

    assert success is True
    assert collector._cache["memory_stats"] == {"percent": 38.0}
    assert any(
        err["component"] == "memory" and "Timeout" in err["error"]
        for err in collector._cache["errors"]
    )


def test_collect_data_records_error_when_cpu_submit_fails(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    _install_fake_executor(
        collector,
        {
            "get_memory_stats": _FakeFuture(value={"percent": 42.0}),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "get_ai_processes": _FakeFuture(value=[]),
        },
        submit_errors={"get_stats": RuntimeError("submit failed")},
    )

    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 0.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=400.0)

    assert success is True
    assert collector._cache["cpu_stats"] is None
    assert collector._cache["memory_stats"] == {"percent": 42.0}
    assert any(
        err["component"] == "cpu_stats" and "submit failed" in err["error"]
        for err in collector._cache["errors"]
    )


def test_collect_data_cleans_up_old_errors_on_schedule(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    _install_fake_executor(collector, {})

    collector._next_runs.update(
        {
            "system": 9999.0,
            "gpu_quick": 9999.0,
            "gpu_full": 9999.0,
            "processes": 9999.0,
            "error_cleanup": 0.0,
        }
    )
    collector._cache["errors"] = [
        {"timestamp": 100.0, "component": "old", "error": "drop-me"},
        {"timestamp": 701.0, "component": "new", "error": "keep-me"},
    ]

    success = collector._collect_data(current_time=1000.0)

    assert success is True
    assert collector._cache["errors"] == [
        {"timestamp": 701.0, "component": "new", "error": "keep-me"}
    ]
    assert collector._next_runs["error_cleanup"] == 1060.0


def test_collect_data_marks_quick_gpu_timeout_non_critical(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    fake_executor = _install_fake_executor(
        collector,
        {
            "get_stats": _FakeFuture(value="cpu-stats"),
            "get_memory_stats": _FakeFuture(value={"percent": 42.0}),
            "get_memory_by_type": _FakeFuture(value={"cached": 128.0}),
            "_collect_quick_gpu_metrics": _FakeFuture(
                error=concurrent.futures.TimeoutError()
            ),
        },
    )
    collector.gpu_monitors = [object()]
    collector._next_runs.update(
        {
            "system": 0.0,
            "gpu_quick": 0.0,
            "gpu_full": 9999.0,
            "processes": 9999.0,
            "error_cleanup": 9999.0,
        }
    )

    success = collector._collect_data(current_time=700.0)

    assert success is True
    assert "_collect_quick_gpu_metrics" in fake_executor.submitted
    assert any(
        err["component"] == "gpu_quick" and "Timeout" in err["error"]
        for err in collector._cache["errors"]
    )


def test_record_error_trims_log_to_max(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector._max_errors = 3

    for idx in range(5):
        collector._record_error("cpu", f"boom-{idx}")

    assert len(collector._cache["errors"]) == 3
    assert collector._cache["errors"][0]["error"] == "boom-2"


def test_update_data_queue_snapshots_gpu_info(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    gpu = GPUInfo(
        index=0,
        name="NVIDIA A100",
        utilization=10.0,
        memory_used=100.0,
        memory_total=1000.0,
        temperature=0.0,
        power_draw=0.0,
        power_limit=0.0,
        processes=[{"pid": 1, "memory": 50.0}],
    )
    collector._cache["gpu_info"] = [(gpu, "nvidia")]
    collector._cache["gpu_processes"] = [{"pid": 1, "memory": 50.0}]
    collector._cache["processes"] = [{"pid": 1, "name": "trainer"}]
    collector._cache["cpu_stats"] = "cpu"
    collector._cache["memory_stats"] = {"percent": 10.0}
    collector._cache["memory_types"] = {"cached": 128.0}

    collector._update_data_queue()

    gpu.utilization = 99.0
    collector._update_gpu_quick_data({"nvidia:0": {"utilization": 42.0}})

    queued = collector.get_data(timeout=0.01)

    assert queued is not None
    assert queued.gpu_info[0][0].utilization == 10.0


def test_update_data_queue_drops_oldest_when_full(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector.vendors = ["nvidia"]

    for idx in (1, 2, 3):
        collector._cache["cpu_stats"] = f"cpu-{idx}"
        collector._cache["memory_stats"] = {"percent": 42.0}
        collector._cache["memory_types"] = {"cached": 128.0}
        collector._cache["gpu_info"] = []
        collector._cache["gpu_processes"] = []
        collector._cache["processes"] = []
        collector._update_data_queue()

    assert collector.queue.qsize() == 2
    assert collector._perf_stats["queue_full_count"] == 1

    first = collector.get_data(timeout=0.01)
    second = collector.get_data(timeout=0.01)
    assert first is not None
    assert second is not None
    assert first.cpu_stats == "cpu-2"
    assert second.cpu_stats == "cpu-3"


def test_update_data_queue_skips_when_essential_data_missing(
    monkeypatch: Any,
) -> None:
    collector = _make_collector(monkeypatch)
    collector._cache["cpu_stats"] = None
    collector._cache["memory_stats"] = {"percent": 42.0}

    collector._update_data_queue()

    assert collector.queue.qsize() == 0
    assert collector._cache["last_update"] == 0


def test_update_gpu_quick_data_updates_only_matching_gpu(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    gpu0 = _FakeGPU(index=0, utilization=5.0, memory_used=10.0, memory_total=100.0)
    gpu1 = _FakeGPU(index=1, utilization=15.0, memory_used=20.0, memory_total=200.0)
    collector._cache["gpu_info"] = [(gpu0, "nvidia"), (gpu1, "amd")]

    collector._update_gpu_quick_data(
        {
            "amd:1": {
                "utilization": 88.0,
                "memory_used": 33.0,
                "memory_total": 222.0,
            }
        }
    )

    assert gpu0.utilization == 5.0
    assert gpu0.memory_used == 10.0
    assert gpu1.utilization == 88.0
    assert gpu1.memory_used == 33.0
    assert gpu1.memory_total == 222.0


def test_update_gpu_quick_data_uses_vendor_scoped_keys(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    gpu0 = _FakeGPU(index=0, utilization=5.0, memory_used=10.0, memory_total=100.0)
    npu0 = _FakeGPU(index=0, utilization=15.0, memory_used=0.0, memory_total=0.0)
    collector._cache["gpu_info"] = [(gpu0, "nvidia"), (npu0, "intel_npu")]

    collector._update_gpu_quick_data(
        {
            "intel_npu:0": {
                "utilization": 61.0,
                "memory_used": 128.0,
                "memory_total": 0.0,
            }
        }
    )

    assert gpu0.utilization == 5.0
    assert gpu0.memory_used == 10.0
    assert npu0.utilization == 61.0
    assert npu0.memory_used == 128.0


def test_stop_clears_running_flag_and_joins_thread(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    fake_executor = _install_fake_executor(collector, {})
    fake_thread = _FakeThread([True, False])
    collector._thread = fake_thread  # type: ignore[assignment]

    assert collector.running.is_set()

    collector.stop()

    assert collector.running.is_set() is False
    assert fake_executor.shutdown_calls == [(False, True)]
    assert fake_thread.join_calls == [5.0]


def test_stop_uses_python38_shutdown_signature(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    fake_executor = _install_fake_executor(collector, {})
    fake_thread = _FakeThread([False])
    collector._thread = fake_thread  # type: ignore[assignment]

    monkeypatch.setattr(sys, "version_info", (3, 8, 18))

    collector.stop()

    assert collector.running.is_set() is False
    assert fake_executor.shutdown_calls == [(False, False)]
    assert fake_thread.join_calls == []


def test_collection_loop_applies_backoff_after_threshold(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector._adaptive_timing = False
    collector._error_threshold = 0
    collector._error_backoff = 1.0

    calls: dict[str, int] = {"collect": 0, "queue": 0}
    times = iter([100.0, 100.0, 100.0, 100.2, 100.2])
    sleep_calls: list[float] = []

    def _fake_time() -> float:
        return next(times, 100.2)

    def _fake_sleep(duration: float) -> None:
        sleep_calls.append(duration)
        collector.running.clear()

    def _fail_collect(_current_time: float) -> bool:
        calls["collect"] += 1
        return False

    def _queue_update() -> None:
        calls["queue"] += 1

    monkeypatch.setattr(data_collector_module.time, "time", _fake_time)
    monkeypatch.setattr(data_collector_module.time, "sleep", _fake_sleep)
    monkeypatch.setattr(collector, "_collect_data", _fail_collect)
    monkeypatch.setattr(collector, "_update_data_queue", _queue_update)

    collector._collection_loop()

    assert calls["collect"] == 1
    assert calls["queue"] == 0
    assert sleep_calls == [0.1]
    assert collector._perf_stats["consecutive_errors"] == 1
    assert collector._error_backoff == 2.0
    assert collector._perf_stats["last_collection_time"] == 100.0


def test_collection_loop_resets_backoff_after_success(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector._adaptive_timing = False
    collector._error_backoff = 8.0
    collector._perf_stats["consecutive_errors"] = 4

    calls: dict[str, int] = {"collect": 0, "queue": 0}
    times = iter([200.0, 200.0, 200.0, 200.2, 200.2])
    sleep_calls: list[float] = []

    def _fake_time() -> float:
        return next(times, 200.2)

    def _fake_sleep(duration: float) -> None:
        sleep_calls.append(duration)
        collector.running.clear()

    def _ok_collect(_current_time: float) -> bool:
        calls["collect"] += 1
        return True

    def _queue_update() -> None:
        calls["queue"] += 1

    monkeypatch.setattr(data_collector_module.time, "time", _fake_time)
    monkeypatch.setattr(data_collector_module.time, "sleep", _fake_sleep)
    monkeypatch.setattr(collector, "_collect_data", _ok_collect)
    monkeypatch.setattr(collector, "_update_data_queue", _queue_update)

    collector._collection_loop()

    assert calls["collect"] == 1
    assert calls["queue"] == 1
    assert sleep_calls == [0.1]
    assert collector._perf_stats["consecutive_errors"] == 0
    assert collector._error_backoff == 1.0


def test_collection_loop_throttles_when_cpu_load_is_high(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector._adaptive_timing = True
    collector.update_interval = 0.4
    collector._throttle_factor = 2.0
    collector._load_threshold = 0.5

    calls: dict[str, int] = {"collect": 0, "queue": 0}
    sleep_calls: list[float] = []
    debug_messages: list[str] = []

    def _fake_cpu_percent(interval: Any = None) -> float:
        assert interval is None
        return 95.0

    def _ok_collect(_current_time: float) -> bool:
        calls["collect"] += 1
        return True

    def _queue_update() -> None:
        calls["queue"] += 1

    def _fake_sleep(duration: float) -> None:
        sleep_calls.append(duration)
        collector.running.clear()

    def _fake_debug(message: str) -> None:
        debug_messages.append(message)

    monkeypatch.setattr(data_collector_module.psutil, "cpu_percent", _fake_cpu_percent)
    monkeypatch.setattr(data_collector_module.time, "sleep", _fake_sleep)
    monkeypatch.setattr(collector, "_collect_data", _ok_collect)
    monkeypatch.setattr(collector, "_update_data_queue", _queue_update)
    monkeypatch.setattr(collector.logger, "debug", _fake_debug)

    collector._collection_loop()

    assert calls["collect"] == 1
    assert calls["queue"] == 1
    assert sleep_calls == [0.1]
    assert any("High system load" in message for message in debug_messages)


def test_collection_loop_uses_normal_interval_when_cpu_load_is_low(
    monkeypatch: Any,
) -> None:
    collector = _make_collector(monkeypatch)
    collector._adaptive_timing = True
    collector.update_interval = 0.06
    collector._throttle_factor = 2.0
    collector._load_threshold = 0.5

    calls: dict[str, int] = {"collect": 0, "queue": 0}
    sleep_calls: list[float] = []
    debug_messages: list[str] = []
    times = iter([300.0, 300.0, 300.0, 300.0])

    def _fake_time() -> float:
        return next(times, 300.0)

    def _fake_cpu_percent(interval: Any = None) -> float:
        assert interval is None
        return 10.0

    def _ok_collect(_current_time: float) -> bool:
        calls["collect"] += 1
        return True

    def _queue_update() -> None:
        calls["queue"] += 1

    def _fake_sleep(duration: float) -> None:
        sleep_calls.append(duration)
        collector.running.clear()

    def _fake_debug(message: str) -> None:
        debug_messages.append(message)

    monkeypatch.setattr(data_collector_module.time, "time", _fake_time)
    monkeypatch.setattr(data_collector_module.psutil, "cpu_percent", _fake_cpu_percent)
    monkeypatch.setattr(data_collector_module.time, "sleep", _fake_sleep)
    monkeypatch.setattr(collector, "_collect_data", _ok_collect)
    monkeypatch.setattr(collector, "_update_data_queue", _queue_update)
    monkeypatch.setattr(collector.logger, "debug", _fake_debug)

    collector._collection_loop()

    assert calls["collect"] == 1
    assert calls["queue"] == 1
    assert sleep_calls == [0.01]
    assert all("High system load" not in message for message in debug_messages)


def test_update_data_queue_recovers_after_put_exception(monkeypatch: Any) -> None:
    collector = _make_collector(monkeypatch)
    collector.vendors = ["nvidia"]
    collector._cache["memory_stats"] = {"percent": 42.0}
    collector._cache["memory_types"] = {"cached": 128.0}
    collector._cache["gpu_info"] = []
    collector._cache["gpu_processes"] = []
    collector._cache["processes"] = []

    # First successful enqueue
    collector._cache["cpu_stats"] = "cpu-1"
    collector._update_data_queue()

    original_put = collector.queue.put_nowait

    def _failing_put(_data: Any) -> None:
        raise RuntimeError("queue write failed")

    # Simulate transient queue write failure
    monkeypatch.setattr(collector.queue, "put_nowait", _failing_put)
    collector._cache["cpu_stats"] = "cpu-2"
    collector._update_data_queue()

    # Restore queue and ensure collector continues enqueuing fresh data
    monkeypatch.setattr(collector.queue, "put_nowait", original_put)
    collector._cache["cpu_stats"] = "cpu-3"
    collector._update_data_queue()

    queued_first = collector.get_data(timeout=0.01)
    queued_second = collector.get_data(timeout=0.01)

    assert queued_first is not None
    assert queued_second is not None
    assert queued_first.cpu_stats == "cpu-1"
    assert queued_second.cpu_stats == "cpu-3"
    assert any(
        err["component"] == "data_queue" and "queue write failed" in err["error"]
        for err in collector._cache["errors"]
    )
