"""Tests for the self-healing platform."""

import pytest

from llmhosts.healing.monitor import HealthCheck, HealthMonitor, ServiceStatus
from llmhosts.healing.remediation import RemediationEngine, RemediationResult


class TestHealthCheck:
    @pytest.mark.asyncio
    async def test_healthy_service(self):
        async def always_ok():
            return True

        check = HealthCheck(name="test", check_fn=always_ok)
        status = await check.check()
        assert status == ServiceStatus.HEALTHY
        assert check.consecutive_failures == 0

    @pytest.mark.asyncio
    async def test_degraded_before_threshold(self):
        async def always_fail():
            return False

        check = HealthCheck(name="test", check_fn=always_fail, failure_threshold=3)
        status = await check.check()
        assert status == ServiceStatus.DEGRADED  # 1 failure, threshold=3
        assert check.consecutive_failures == 1

    @pytest.mark.asyncio
    async def test_down_after_threshold(self):
        async def always_fail():
            return False

        check = HealthCheck(name="test", check_fn=always_fail, failure_threshold=2)
        await check.check()
        status = await check.check()
        assert status == ServiceStatus.DOWN

    @pytest.mark.asyncio
    async def test_recovery_resets_failures(self):
        call_count = 0

        async def flaky():
            nonlocal call_count
            call_count += 1
            return call_count > 2  # fails twice then succeeds

        check = HealthCheck(name="test", check_fn=flaky)
        await check.check()
        await check.check()
        status = await check.check()
        assert status == ServiceStatus.HEALTHY
        assert check.consecutive_failures == 0

    @pytest.mark.asyncio
    async def test_exception_counts_as_failure(self):
        async def raises():
            raise RuntimeError("boom")

        check = HealthCheck(name="test", check_fn=raises)
        status = await check.check()
        assert status == ServiceStatus.DEGRADED
        assert check.consecutive_failures == 1


class TestHealthMonitor:
    @pytest.mark.asyncio
    async def test_check_all_returns_statuses(self):
        async def ok():
            return True

        monitor = HealthMonitor()
        monitor.register(HealthCheck("svc1", ok))
        monitor.register(HealthCheck("svc2", ok))
        results = await monitor.check_all()
        assert results["svc1"] == ServiceStatus.HEALTHY
        assert results["svc2"] == ServiceStatus.HEALTHY


class TestRemediationEngine:
    @pytest.mark.asyncio
    async def test_remediate_unknown_service(self):
        engine = RemediationEngine()
        result = await engine.remediate("unknown-service-xyz")
        assert isinstance(result, RemediationResult)
        assert result.service == "unknown-service-xyz"

    @pytest.mark.asyncio
    async def test_circuit_breaker_trips(self):
        engine = RemediationEngine()
        engine.MAX_ATTEMPTS_PER_HOUR = 2
        # Use _record_attempt directly to pre-fill the log
        for _ in range(2):
            engine._record_attempt("ollama")
        result = await engine.remediate("ollama")
        assert not result.success
        assert result.action == "circuit_breaker"

    @pytest.mark.asyncio
    async def test_healing_history(self):
        engine = RemediationEngine()
        await engine.remediate("cache")
        history = engine.healing_history()
        assert len(history) == 1
        assert history[0]["service"] == "cache"

    @pytest.mark.asyncio
    async def test_healing_stats(self):
        engine = RemediationEngine()
        await engine.remediate("cache")
        await engine.remediate("proxy")
        stats = engine.healing_stats()
        assert stats["total_actions"] == 2
        assert "cache" in stats["services_seen"]

    def test_remediation_result_to_dict(self):
        r = RemediationResult(service="s", action="a", success=True, message="m")
        d = r.to_dict()
        assert d["service"] == "s"
        assert "timestamp" in d

    @pytest.mark.asyncio
    async def test_remediate_cache(self):
        engine = RemediationEngine()
        result = await engine.remediate("cache")
        assert result.success is True
        assert result.action == "soft_reset"

    @pytest.mark.asyncio
    async def test_remediate_proxy(self):
        engine = RemediationEngine()
        result = await engine.remediate("proxy")
        assert result.success is True
