from datetime import timedelta

from freezegun import freeze_time

from yslow.ui.watch import WatchState, _render, notify

from .conftest import T0, make_check


class TestWatchStateConsecutiveOk:
    def test_all_healthy(self):
        state = WatchState(interval=30)
        for i in range(5):
            state.append(
                make_check(healthy=True, timestamp=T0 + timedelta(seconds=i * 30))
            )
        assert state.consecutive_ok == 5

    def test_recent_error(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True, timestamp=T0))
        state.append(make_check(healthy=False, timestamp=T0 + timedelta(seconds=30)))
        assert state.consecutive_ok == 0

    def test_empty(self):
        state = WatchState(interval=30)
        assert state.consecutive_ok == 0


class TestWatchStateConsecutiveProblem:
    def test_all_healthy(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True))
        assert state.consecutive_problem == 0

    def test_consecutive_problems(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True, timestamp=T0))
        state.append(make_check(healthy=False, timestamp=T0 + timedelta(seconds=30)))
        state.append(make_check(healthy=False, timestamp=T0 + timedelta(seconds=60)))
        assert state.consecutive_problem == 2

    def test_empty(self):
        state = WatchState(interval=30)
        assert state.consecutive_problem == 0


class TestWatchStateHasHadErrors:
    def test_no_errors(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True))
        assert state.has_had_errors is False

    def test_past_error(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=False))
        state.append(make_check(healthy=True))
        assert state.has_had_errors is True


class TestWatchStateLastError:
    def test_no_errors(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True))
        assert state.last_error is None

    def test_current_error(self):
        state = WatchState(interval=30)
        err = make_check(healthy=False, timestamp=T0 + timedelta(seconds=30))
        state.append(make_check(healthy=True, timestamp=T0))
        state.append(err)
        assert state.last_error is err

    def test_past_error(self):
        state = WatchState(interval=30)
        err = make_check(healthy=False, timestamp=T0 + timedelta(seconds=30))
        state.append(make_check(healthy=True, timestamp=T0))
        state.append(err)
        state.append(make_check(healthy=True, timestamp=T0 + timedelta(seconds=60)))
        assert state.last_error is err


class TestWatchStateBufferLimit:
    def test_trims_to_max(self):
        state = WatchState(interval=30, max_results=5)
        for i in range(10):
            state.append(make_check(timestamp=T0 + timedelta(seconds=i)))
        assert len(state.results) == 5


class TestNotify:
    def test_no_notify_when_healthy(self, capsys):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True))
        notify(state)
        assert "\a" not in capsys.readouterr().out

    def test_no_notify_on_single_check(self, capsys):
        state = WatchState(interval=30)
        state.append(make_check(healthy=False))
        notify(state)
        assert "\a" not in capsys.readouterr().out

    def test_notify_on_transition(self, capsys):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True))
        state.append(make_check(healthy=False))
        notify(state)
        assert "\a" in capsys.readouterr().out

    def test_no_repeat_notify(self, capsys):
        state = WatchState(interval=30)
        state.append(make_check(healthy=False))
        state.append(make_check(healthy=False))
        notify(state)
        assert "\a" not in capsys.readouterr().out


class TestRender:
    @freeze_time(T0)
    def test_stable_dashboard(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=True, timestamp=T0))
        output = _render(state)
        assert "Nope!" in output
        assert "LAN:" in output

    @freeze_time(T0)
    def test_problem_dashboard(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=False, timestamp=T0))
        output = _render(state)
        assert "YES!" in output
        assert "test problem" in output
        assert "ongoing" in output

    @freeze_time(T0)
    def test_unstable_after_recovery(self):
        state = WatchState(interval=30)
        state.append(make_check(healthy=False, timestamp=T0 - timedelta(seconds=60)))
        state.append(make_check(healthy=True, timestamp=T0))
        output = _render(state)
        assert "Sorta?" in output
        assert "test problem" in output
        assert "ago" in output
        assert "!!" not in output
