from yslow.models import ProbeResult, Severity
from yslow.diagnosis import analyze


def _result(label="test", host="1.1.1.1", sent=5, lost=0, rtts=None):
    return ProbeResult(host=host, label=label, sent=sent, lost=lost, rtts=rtts or [])


class TestAnalyzeHealthy:
    def test_good_connection(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, alerts = analyze(gw, inet, [])
        assert issues == []
        assert alerts == []

    def test_no_gateway_detected_good_internet(self):
        """When gateway isn't detected (None), no warning — it's just unavailable info."""
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, _alerts = analyze(None, inet, [])
        assert not any("gateway" in i.name for i in issues)


class TestAnalyzeNoConnectivity:
    def test_nothing_reachable(self):
        gw = _result("gw", sent=5, lost=5)
        inet = [_result("dns", sent=5, lost=5)]
        issues, _alerts = analyze(gw, inet, [])
        assert len(issues) == 1
        assert "no targets reachable" in issues[0].name

    def test_no_gateway_nothing_reachable(self):
        inet = [_result("dns", sent=5, lost=5)]
        issues, _alerts = analyze(None, inet, [])
        assert "no targets reachable" in issues[0].name


class TestAnalyzeGatewayProblems:
    def test_high_gateway_latency(self):
        gw = _result("gw", host="192.168.1.1", rtts=[15.0, 20.0, 18.0, 16.0, 17.0])
        inet = [_result("dns", rtts=[30.0, 35.0, 32.0, 31.0, 33.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(
            i.name == "unstable connection" and i.source == "LAN" for i in issues
        )
        assert issues[0].severity == Severity.ERROR
        assert any(a.host == gw.host and a.metric == "rtt" for a in alerts)

    def test_elevated_gateway_latency(self):
        gw = _result("gw", rtts=[4.0, 4.5, 4.2, 4.1, 4.3])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(i.name == "elevated latency" and i.source == "LAN" for i in issues)
        assert issues[0].severity == Severity.WARNING
        assert any(a.host == gw.host and a.metric == "rtt" for a in alerts)

    def test_gateway_packet_loss(self):
        gw = _result("gw", sent=5, lost=2, rtts=[1.0, 1.2, 1.1])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(i.name == "packet loss to gateway" for i in issues)
        assert any(a.host == gw.host and a.metric == "loss" for a in alerts)

    def test_gateway_jitter(self):
        gw = _result("gw", rtts=[1.0, 1.0, 1.0, 1.0, 50.0])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(
            i.name == "unstable connection" and i.source == "LAN" for i in issues
        )
        assert any(a.host == gw.host and a.metric == "jitter" for a in alerts)


class TestAnalyzeISPProblems:
    def test_high_isp_latency(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", rtts=[200.0, 210.0, 190.0, 205.0, 195.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(
            i.name == "high latency beyond gateway" and i.source == "ISP"
            for i in issues
        )
        assert any(a.host == "1.1.1.1" and a.metric == "rtt" for a in alerts)

    def test_moderate_isp_latency(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", rtts=[60.0, 65.0, 62.0, 61.0, 63.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(
            i.name == "moderate latency beyond gateway" and i.source == "ISP"
            for i in issues
        )
        matching = [i for i in issues if "moderate" in i.name]
        assert matching[0].severity == Severity.WARNING

    def test_internet_loss_beyond_gateway(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", sent=5, lost=1, rtts=[20.0, 21.0, 19.0, 20.5])]
        issues, alerts = analyze(gw, inet, [])
        assert any(
            i.name == "packet loss beyond local network" and i.source == "ISP"
            for i in issues
        )
        assert any(a.host == "1.1.1.1" and a.metric == "loss" for a in alerts)

    def test_internet_jitter(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", rtts=[10.0, 10.0, 10.0, 10.0, 200.0])]
        issues, alerts = analyze(gw, inet, [])
        assert any(i.name == "high jitter" and i.source == "ISP" for i in issues)


class TestAnalyzeNoGateway:
    def test_high_latency_no_gateway(self):
        inet = [_result("dns", rtts=[200.0, 210.0, 190.0, 205.0, 195.0])]
        issues, _alerts = analyze(None, inet, [])
        assert any(i.name == "high latency" for i in issues)

    def test_moderate_latency_no_gateway(self):
        inet = [_result("dns", rtts=[60.0, 65.0, 62.0, 61.0, 63.0])]
        issues, _alerts = analyze(None, inet, [])
        assert any(i.name == "moderate latency" for i in issues)

    def test_gateway_unreachable_internet_works(self):
        gw = _result("gw", sent=5, lost=5)
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        issues, _alerts = analyze(gw, inet, [])
        assert any(
            i.name == "gateway unreachable" and i.cause == "unusual routing setup"
            for i in issues
        )


class TestMetricAlerts:
    def test_no_alerts_when_healthy(self):
        gw = _result("gw", rtts=[1.0, 1.2, 1.1, 1.0, 1.3])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        _issues, alerts = analyze(gw, inet, [])
        assert alerts == []

    def test_gateway_loss_alert(self):
        gw = _result("gw", host="192.168.1.1", sent=5, lost=2, rtts=[1.0, 1.2, 1.1])
        inet = [_result("dns", rtts=[20.0, 21.0, 19.0, 20.5, 20.0])]
        _issues, alerts = analyze(gw, inet, [])
        assert any(
            a.host == "192.168.1.1"
            and a.metric == "loss"
            and a.severity == Severity.ERROR
            for a in alerts
        )
