"""Tests for the sovereignty metrics store and API routes.

Refs: #981
"""

from unittest.mock import AsyncMock, patch

import pytest

from infrastructure.sovereignty_metrics import (
    GRADUATION_TARGETS,
    SovereigntyMetric,
    SovereigntyMetricsStore,
    emit_sovereignty_metric,
)


@pytest.fixture
def store(tmp_path):
    """Create a fresh sovereignty metrics store with a temp DB."""
    return SovereigntyMetricsStore(db_path=tmp_path / "test_sov.db")


class TestSovereigntyMetricsStore:
    def test_record_and_get_latest(self, store):
        metric = SovereigntyMetric(metric_type="cache_hit_rate", value=0.42)
        store.record(metric)

        results = store.get_latest("cache_hit_rate", limit=10)
        assert len(results) == 1
        assert results[0]["value"] == 0.42

    def test_get_latest_returns_most_recent_first(self, store):
        for val in [0.1, 0.2, 0.3]:
            store.record(SovereigntyMetric(metric_type="cache_hit_rate", value=val))

        results = store.get_latest("cache_hit_rate", limit=10)
        assert len(results) == 3
        assert results[0]["value"] == 0.3  # most recent first

    def test_get_latest_respects_limit(self, store):
        for i in range(10):
            store.record(SovereigntyMetric(metric_type="api_cost", value=float(i)))

        results = store.get_latest("api_cost", limit=3)
        assert len(results) == 3

    def test_get_latest_filters_by_type(self, store):
        store.record(SovereigntyMetric(metric_type="cache_hit_rate", value=0.5))
        store.record(SovereigntyMetric(metric_type="api_cost", value=1.20))

        results = store.get_latest("cache_hit_rate")
        assert len(results) == 1
        assert results[0]["value"] == 0.5

    def test_get_summary_empty(self, store):
        summary = store.get_summary()
        assert "cache_hit_rate" in summary
        assert summary["cache_hit_rate"]["current"] is None
        assert summary["cache_hit_rate"]["phase"] == "pre-start"

    def test_get_summary_with_data(self, store):
        store.record(SovereigntyMetric(metric_type="cache_hit_rate", value=0.85))
        store.record(SovereigntyMetric(metric_type="api_cost", value=0.08))

        summary = store.get_summary()
        assert summary["cache_hit_rate"]["current"] == 0.85
        assert summary["cache_hit_rate"]["phase"] == "month3"
        assert summary["api_cost"]["current"] == 0.08
        assert summary["api_cost"]["phase"] == "month3"

    def test_get_summary_graduation(self, store):
        store.record(SovereigntyMetric(metric_type="cache_hit_rate", value=0.95))
        summary = store.get_summary()
        assert summary["cache_hit_rate"]["phase"] == "graduated"

    def test_alert_on_high_api_cost(self, store):
        """API cost above threshold triggers an alert."""
        with patch("infrastructure.sovereignty_metrics.settings") as mock_settings:
            mock_settings.sovereignty_api_cost_alert_threshold = 1.00
            mock_settings.db_busy_timeout_ms = 5000
            store.record(SovereigntyMetric(metric_type="api_cost", value=2.50))

        alerts = store.get_alerts(unacknowledged_only=True)
        assert len(alerts) == 1
        assert alerts[0]["alert_type"] == "api_cost_exceeded"
        assert alerts[0]["value"] == 2.50

    def test_no_alert_below_threshold(self, store):
        """API cost below threshold does not trigger an alert."""
        with patch("infrastructure.sovereignty_metrics.settings") as mock_settings:
            mock_settings.sovereignty_api_cost_alert_threshold = 1.00
            mock_settings.db_busy_timeout_ms = 5000
            store.record(SovereigntyMetric(metric_type="api_cost", value=0.50))

        alerts = store.get_alerts(unacknowledged_only=True)
        assert len(alerts) == 0

    def test_acknowledge_alert(self, store):
        with patch("infrastructure.sovereignty_metrics.settings") as mock_settings:
            mock_settings.sovereignty_api_cost_alert_threshold = 0.50
            mock_settings.db_busy_timeout_ms = 5000
            store.record(SovereigntyMetric(metric_type="api_cost", value=1.00))

        alerts = store.get_alerts(unacknowledged_only=True)
        assert len(alerts) == 1

        store.acknowledge_alert(alerts[0]["id"])
        assert len(store.get_alerts(unacknowledged_only=True)) == 0
        assert len(store.get_alerts(unacknowledged_only=False)) == 1

    def test_metadata_preserved(self, store):
        store.record(
            SovereigntyMetric(
                metric_type="cache_hit_rate",
                value=0.5,
                metadata={"source": "research_orchestrator"},
            )
        )
        results = store.get_latest("cache_hit_rate")
        assert results[0]["metadata"]["source"] == "research_orchestrator"

    def test_summary_trend_data(self, store):
        for v in [0.1, 0.2, 0.3]:
            store.record(SovereigntyMetric(metric_type="cache_hit_rate", value=v))

        summary = store.get_summary()
        trend = summary["cache_hit_rate"]["trend"]
        assert len(trend) == 3
        assert trend[0]["v"] == 0.1  # oldest first (reversed)
        assert trend[-1]["v"] == 0.3

    def test_graduation_targets_complete(self):
        """All expected metric types have graduation targets."""
        expected = {
            "cache_hit_rate",
            "api_cost",
            "time_to_report",
            "human_involvement",
            "local_artifacts",
        }
        assert set(GRADUATION_TARGETS.keys()) == expected


class TestEmitSovereigntyMetric:
    @pytest.mark.asyncio
    async def test_emit_records_and_publishes(self, tmp_path):
        """emit_sovereignty_metric records to store and publishes event."""
        with (
            patch("infrastructure.sovereignty_metrics._store", None),
            patch(
                "infrastructure.sovereignty_metrics.DB_PATH",
                tmp_path / "emit_test.db",
            ),
            patch("infrastructure.events.bus.emit", new_callable=AsyncMock) as mock_emit,
        ):
            await emit_sovereignty_metric("cache_hit_rate", 0.75, {"source": "test"})

            mock_emit.assert_called_once()
            call_args = mock_emit.call_args
            assert call_args[0][0] == "sovereignty.metric.cache_hit_rate"


class TestSovereigntyMetricsRoutes:
    def test_metrics_api_returns_200(self, client):
        response = client.get("/sovereignty/metrics")
        assert response.status_code == 200
        data = response.json()
        assert "metrics" in data
        assert "alerts" in data
        assert "targets" in data

    def test_metrics_panel_returns_html(self, client):
        response = client.get("/sovereignty/metrics/panel")
        assert response.status_code == 200
        assert "text/html" in response.headers["content-type"]

    def test_alerts_api_returns_200(self, client):
        response = client.get("/sovereignty/alerts")
        assert response.status_code == 200
        data = response.json()
        assert "alerts" in data
        assert "unacknowledged" in data
