"""Tests for the LG-13 log-volume-control filters."""

from __future__ import annotations

import logging

from mgf.common.observability import DedupFilter, RateLimitFilter, SampleFilter


def _rec(name: str = "t", level: int = logging.INFO, msg: str = "m") -> logging.LogRecord:
    return logging.LogRecord(name, level, "f.py", 1, msg, None, None)


class TestRateLimitFilter:
    def test_allows_up_to_max_then_drops(self) -> None:
        f = RateLimitFilter(max_per_second=3)
        results = [f.filter(_rec()) for _ in range(5)]
        assert results == [True, True, True, False, False]

    def test_per_logger_level_keying(self) -> None:
        f = RateLimitFilter(max_per_second=1)
        assert f.filter(_rec(name="a")) is True
        assert f.filter(_rec(name="b")) is True  # different logger → own budget
        assert f.filter(_rec(name="a")) is False

    def test_window_reset_re_allows(self, monkeypatch) -> None:
        import mgf.common.observability.log_filters as mod

        t = [100.0]
        monkeypatch.setattr(mod.time, "monotonic", lambda: t[0])
        f = RateLimitFilter(max_per_second=1)
        assert f.filter(_rec()) is True
        assert f.filter(_rec()) is False
        t[0] += 1.5  # advance past the window
        assert f.filter(_rec()) is True


class TestSampleFilter:
    def test_keeps_one_in_n(self, monkeypatch) -> None:
        import mgf.common.observability.log_filters as mod

        # Force the "kept" branch then the "dropped" branch deterministically.
        monkeypatch.setattr(mod.random, "randint", lambda a, b: 1)
        assert SampleFilter(sample_rate=100).filter(_rec()) is True
        monkeypatch.setattr(mod.random, "randint", lambda a, b: 2)
        assert SampleFilter(sample_rate=100).filter(_rec()) is False

    def test_rejects_bad_rate(self) -> None:
        import pytest

        with pytest.raises(ValueError, match=">= 1"):
            SampleFilter(sample_rate=0)


class TestDedupFilter:
    def test_suppresses_duplicates_in_window(self, monkeypatch) -> None:
        import mgf.common.observability.log_filters as mod

        t = [0.0]
        monkeypatch.setattr(mod.time, "monotonic", lambda: t[0])
        f = DedupFilter(window_s=5.0)
        assert f.filter(_rec(msg="dup")) is True
        assert f.filter(_rec(msg="dup")) is False  # within window
        assert f.filter(_rec(msg="other")) is True  # different msg
        t[0] = 6.0  # past window
        assert f.filter(_rec(msg="dup")) is True  # window expired → allowed

    def test_prunes_expired_keys(self, monkeypatch) -> None:
        import mgf.common.observability.log_filters as mod

        t = [0.0]
        monkeypatch.setattr(mod.time, "monotonic", lambda: t[0])
        f = DedupFilter(window_s=1.0)
        for i in range(50):
            f.filter(_rec(msg=f"m{i}"))
        t[0] = 100.0  # all expired
        f.filter(_rec(msg="new"))
        # memory bounded — expired keys pruned, only the live one remains.
        assert len(f._last) == 1
