"""Tests for ``mgf.apiprobe.checks.quality``."""

from __future__ import annotations

import pytest
from tests.unit.checks.conftest import empty_ctx, make_response

from mgf.apiprobe.checks.quality import (
    BodySizeOverBudget,
    LatencyOverBudget,
    LatencySuspiciousZero,
)
from mgf.apiprobe.core.finding import RootCause, Severity
from mgf.apiprobe.core.probe import Probe


class TestLatencyOverBudget:
    def test_silent_when_no_budget(self) -> None:
        probe = Probe.get("https://x/y").id("p1")
        resp = make_response(elapsed_ms=10000.0)
        findings = list(LatencyOverBudget().run(probe, resp, empty_ctx()))
        assert findings == []

    def test_silent_at_budget(self) -> None:
        probe = Probe.get("https://x/y").id("p1").expect_latency_ms(500)
        resp = make_response(elapsed_ms=500.0)
        findings = list(LatencyOverBudget().run(probe, resp, empty_ctx()))
        assert findings == []

    def test_silent_under_budget(self) -> None:
        probe = Probe.get("https://x/y").id("p1").expect_latency_ms(500)
        resp = make_response(elapsed_ms=200.0)
        findings = list(LatencyOverBudget().run(probe, resp, empty_ctx()))
        assert findings == []

    def test_emits_over_budget(self) -> None:
        probe = Probe.get("https://x/y").id("p1").expect_latency_ms(500)
        resp = make_response(elapsed_ms=750.0)
        findings = list(LatencyOverBudget().run(probe, resp, empty_ctx()))
        assert len(findings) == 1
        f = findings[0]
        assert f.code == "quality.latency.over-budget"
        assert f.severity is Severity.WARN
        assert f.root_cause is RootCause.PROVIDER_QUALITY
        assert f.evidence["over_by_ms"] == 250.0


class TestLatencySuspiciousZero:
    def test_silent_on_positive_latency(self) -> None:
        probe = Probe.get("https://x/y").id("p1")
        resp = make_response(elapsed_ms=10.0)
        findings = list(LatencySuspiciousZero().run(probe, resp, empty_ctx()))
        assert findings == []

    @pytest.mark.parametrize("bad", [0.0, -1.0, -100.0])
    def test_emits_on_zero_or_negative(self, bad: float) -> None:
        probe = Probe.get("https://x/y").id("p1")
        resp = make_response(elapsed_ms=bad)
        findings = list(LatencySuspiciousZero().run(probe, resp, empty_ctx()))
        assert len(findings) == 1
        f = findings[0]
        assert f.code == "quality.latency.suspicious-zero"
        assert f.severity is Severity.CRITICAL
        assert f.root_cause is RootCause.INFRASTRUCTURE


class TestBodySizeOverBudget:
    def test_default_threshold_silent_on_small_body(self) -> None:
        probe = Probe.get("https://x/y").id("p1")
        resp = make_response(body=b"small")
        findings = list(BodySizeOverBudget().run(probe, resp, empty_ctx()))
        assert findings == []

    def test_emits_on_overlimit_body(self) -> None:
        probe = Probe.get("https://x/y").id("p1")
        resp = make_response(body=b"x" * 100)
        # Use a tight threshold to force the finding without a 5MB allocation.
        check = BodySizeOverBudget(threshold_bytes=50)
        findings = list(check.run(probe, resp, empty_ctx()))
        assert len(findings) == 1
        f = findings[0]
        assert f.code == "quality.body-size.over-budget"
        assert f.evidence["body_bytes"] == 100
        assert f.evidence["threshold_bytes"] == 50

    def test_silent_at_threshold(self) -> None:
        probe = Probe.get("https://x/y").id("p1")
        check = BodySizeOverBudget(threshold_bytes=10)
        resp = make_response(body=b"x" * 10)
        findings = list(check.run(probe, resp, empty_ctx()))
        assert findings == []

    @pytest.mark.parametrize("bad", [0, -1])
    def test_construct_rejects_non_positive_threshold(self, bad: int) -> None:
        with pytest.raises(ValueError, match="positive"):
            BodySizeOverBudget(threshold_bytes=bad)
