"""
Tests for Phase 54: Platform Metrics Collection and Time-Series Storage.
"""

import json
import time
from datetime import datetime, timezone, timedelta

import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    """Redirect all metrics storage to tmp_path."""
    metrics_dir = tmp_path / "metrics"
    metrics_dir.mkdir(parents=True, exist_ok=True)

    import brynq.server as srv
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)

    import brynq.metrics_collector as mc
    monkeypatch.setattr(mc, "METRICS_DIR", metrics_dir)


# ── Record Metric ────────────────────────────────────────────────────────

class TestRecordMetric:
    def test_record_basic(self):
        from brynq.metrics_collector import record_metric
        p = record_metric("cpu_usage", 42.5)
        assert p["name"] == "cpu_usage"
        assert p["value"] == 42.5
        assert "timestamp" in p

    def test_record_with_tags(self):
        from brynq.metrics_collector import record_metric
        p = record_metric("latency", 100, tags={"service": "api", "region": "us"})
        assert p["tags"]["service"] == "api"
        assert p["tags"]["region"] == "us"

    def test_record_multiple_points(self):
        from brynq.metrics_collector import record_metric, get_metric
        record_metric("counter", 1)
        record_metric("counter", 2)
        record_metric("counter", 3)
        points = get_metric("counter")
        assert len(points) == 3

    def test_record_creates_file(self):
        from brynq.metrics_collector import record_metric, METRICS_DIR
        record_metric("new_metric", 1)
        # File should exist
        files = list(METRICS_DIR.glob("*.jsonl"))
        assert len(files) >= 1


# ── Get Metric ───────────────────────────────────────────────────────────

class TestGetMetric:
    def test_get_empty(self):
        from brynq.metrics_collector import get_metric
        assert get_metric("nonexistent") == []

    def test_get_all_points(self):
        from brynq.metrics_collector import record_metric, get_metric
        for i in range(5):
            record_metric("test", float(i))
        points = get_metric("test")
        assert len(points) == 5

    def test_get_filtered_by_since(self):
        from brynq.metrics_collector import record_metric, get_metric
        # Record with known timestamps
        record_metric("test", 1)
        record_metric("test", 2)
        now = datetime.now(timezone.utc).isoformat()
        record_metric("test", 3)
        # Since now should get at most the last one (race condition possible, so >= 0)
        points = get_metric("test", since=now)
        assert len(points) <= 3  # basic sanity

    def test_get_with_interval_aggregation(self):
        from brynq.metrics_collector import record_metric, get_metric
        for i in range(10):
            record_metric("agg_test", float(i))
        result = get_metric("agg_test", interval="1h")
        assert len(result) >= 1
        bucket = result[0]
        assert "avg" in bucket
        assert "min" in bucket
        assert "max" in bucket
        assert "count" in bucket

    def test_get_interval_sums_correctly(self):
        from brynq.metrics_collector import record_metric, get_metric
        record_metric("sum_test", 10.0)
        record_metric("sum_test", 20.0)
        record_metric("sum_test", 30.0)
        result = get_metric("sum_test", interval="1h")
        assert len(result) == 1
        assert result[0]["sum"] == 60.0
        assert result[0]["count"] == 3
        assert result[0]["avg"] == 20.0


# ── Get Latest ───────────────────────────────────────────────────────────

class TestGetLatest:
    def test_latest_returns_last(self):
        from brynq.metrics_collector import record_metric, get_latest
        record_metric("x", 1)
        record_metric("x", 2)
        record_metric("x", 99)
        latest = get_latest("x")
        assert latest["value"] == 99

    def test_latest_empty_returns_none(self):
        from brynq.metrics_collector import get_latest
        assert get_latest("ghost") is None


# ── List Metrics ─────────────────────────────────────────────────────────

class TestListMetrics:
    def test_list_empty(self):
        from brynq.metrics_collector import list_metrics
        assert list_metrics() == []

    def test_list_returns_names(self):
        from brynq.metrics_collector import record_metric, list_metrics
        record_metric("alpha", 1)
        record_metric("beta", 2)
        names = list_metrics()
        assert "alpha" in names
        assert "beta" in names

    def test_list_no_duplicates(self):
        from brynq.metrics_collector import record_metric, list_metrics
        record_metric("x", 1)
        record_metric("x", 2)
        names = list_metrics()
        assert names.count("x") == 1


# ── Aggregate ────────────────────────────────────────────────────────────

class TestAggregate:
    def test_aggregate_avg(self):
        from brynq.metrics_collector import record_metric, aggregate
        record_metric("test", 10.0)
        record_metric("test", 20.0)
        record_metric("test", 30.0)
        result = aggregate("test", period_hours=1, fn="avg")
        assert result == 20.0

    def test_aggregate_min(self):
        from brynq.metrics_collector import record_metric, aggregate
        record_metric("test", 5.0)
        record_metric("test", 15.0)
        result = aggregate("test", period_hours=1, fn="min")
        assert result == 5.0

    def test_aggregate_max(self):
        from brynq.metrics_collector import record_metric, aggregate
        record_metric("test", 5.0)
        record_metric("test", 15.0)
        result = aggregate("test", period_hours=1, fn="max")
        assert result == 15.0

    def test_aggregate_sum(self):
        from brynq.metrics_collector import record_metric, aggregate
        record_metric("test", 10.0)
        record_metric("test", 20.0)
        result = aggregate("test", period_hours=1, fn="sum")
        assert result == 30.0

    def test_aggregate_count(self):
        from brynq.metrics_collector import record_metric, aggregate
        record_metric("test", 1.0)
        record_metric("test", 2.0)
        record_metric("test", 3.0)
        result = aggregate("test", period_hours=1, fn="count")
        assert result == 3

    def test_aggregate_invalid_fn(self):
        from brynq.metrics_collector import aggregate
        with pytest.raises(ValueError, match="Invalid function"):
            aggregate("test", fn="median")

    def test_aggregate_no_data_returns_none(self):
        from brynq.metrics_collector import aggregate
        result = aggregate("ghost", period_hours=1, fn="avg")
        assert result is None


# ── Dashboard Data ───────────────────────────────────────────────────────

class TestDashboardData:
    def test_dashboard_empty(self):
        from brynq.metrics_collector import get_dashboard_data
        data = get_dashboard_data()
        assert data["active_agents"] == 0
        assert data["tasks_per_hour"] == 0
        assert data["success_rate"] == 0.0
        assert data["avg_response_time_ms"] == 0.0

    def test_dashboard_with_data(self):
        from brynq.metrics_collector import record_metric, get_dashboard_data
        record_metric("active_agents", 5)
        record_metric("tasks_completed", 1)
        record_metric("task_success", 1.0)
        record_metric("response_time_ms", 150.0)
        data = get_dashboard_data()
        assert data["active_agents"] == 5
        assert data["tasks_per_hour"] >= 1
        assert data["success_rate"] == 100.0
        assert data["avg_response_time_ms"] == 150.0

    def test_dashboard_success_rate_partial(self):
        from brynq.metrics_collector import record_metric, get_dashboard_data
        record_metric("task_success", 1.0)
        record_metric("task_success", 0.0)
        data = get_dashboard_data()
        assert data["success_rate"] == 50.0


# ── Parse Interval ───────────────────────────────────────────────────────

class TestParseInterval:
    def test_parse_seconds(self):
        from brynq.metrics_collector import _parse_interval
        assert _parse_interval("30s") == 30

    def test_parse_minutes(self):
        from brynq.metrics_collector import _parse_interval
        assert _parse_interval("5m") == 300

    def test_parse_hours(self):
        from brynq.metrics_collector import _parse_interval
        assert _parse_interval("1h") == 3600

    def test_parse_days(self):
        from brynq.metrics_collector import _parse_interval
        assert _parse_interval("1d") == 86400

    def test_parse_invalid_unit(self):
        from brynq.metrics_collector import _parse_interval
        with pytest.raises(ValueError, match="Unknown interval unit"):
            _parse_interval("1x")
