"""Tests for orchestration.metrics_export -- Prometheus, health, JSON."""

from __future__ import annotations

import json
import time

import pytest

from orxaq.orchestration.metrics import (
    MetricsCollector,
    TaskMetric,
    TaskStatus,
)
from orxaq.orchestration.metrics_export import (
    HealthCheck,
    MetricsExportFormat,
    PrometheusExporter,
    export_metrics,
)

# ── Helpers ──────────────────────────────────────────────


def _make_task(
    task_id: str = "t1",
    provider: str = "vllm",
    worker: str = "w1",
    status: TaskStatus = TaskStatus.COMPLETED,
    duration: float = 0.05,
    cost: float = 0.001,
    tokens_in: int = 100,
    tokens_out: int = 50,
) -> TaskMetric:
    """Create a TaskMetric with sensible defaults."""
    now = time.time()
    return TaskMetric(
        task_id=task_id,
        task_type="code_review",
        provider=provider,
        worker=worker,
        status=status,
        start_time=now - duration,
        end_time=now,
        tokens_in=tokens_in,
        tokens_out=tokens_out,
        cost_usd=cost,
    )


def _collector_with_tasks(
    n_completed: int = 5,
    n_failed: int = 0,
    provider: str = "vllm",
) -> MetricsCollector:
    """Return a MetricsCollector pre-loaded with tasks."""
    c = MetricsCollector()
    for i in range(n_completed):
        c.record_task(
            _make_task(
                task_id=f"ok-{i}",
                provider=provider,
                status=TaskStatus.COMPLETED,
            )
        )
    for i in range(n_failed):
        c.record_task(
            _make_task(
                task_id=f"fail-{i}",
                provider=provider,
                status=TaskStatus.FAILED,
                cost=0.0,
            )
        )
    return c


# ── PrometheusExporter ───────────────────────────────────


class TestPrometheusExporterEmpty:
    """Prometheus output with an empty collector."""

    def test_export_empty(self) -> None:
        c = MetricsCollector()
        text = PrometheusExporter(c).export()
        assert "orxaq_tasks_total 0" in text
        assert "orxaq_tasks_completed_total 0" in text
        assert "orxaq_tasks_failed_total 0" in text

    def test_export_has_help_and_type(self) -> None:
        c = MetricsCollector()
        text = PrometheusExporter(c).export()
        assert "# HELP orxaq_tasks_total" in text
        assert "# TYPE orxaq_tasks_total counter" in text

    def test_export_ends_with_newline(self) -> None:
        c = MetricsCollector()
        text = PrometheusExporter(c).export()
        assert text.endswith("\n")


class TestPrometheusExporterWithTasks:
    """Prometheus output with recorded tasks."""

    def test_task_counts(self) -> None:
        c = _collector_with_tasks(n_completed=3, n_failed=1)
        text = PrometheusExporter(c).export()
        assert "orxaq_tasks_total 4" in text
        assert "orxaq_tasks_completed_total 3" in text
        assert "orxaq_tasks_failed_total 1" in text

    def test_error_rate(self) -> None:
        c = _collector_with_tasks(n_completed=3, n_failed=1)
        text = PrometheusExporter(c).export()
        assert "orxaq_error_rate 0.25" in text

    def test_cost_present(self) -> None:
        c = _collector_with_tasks(n_completed=2)
        text = PrometheusExporter(c).export()
        assert "orxaq_cost_usd_total" in text

    def test_provider_labels(self) -> None:
        c = _collector_with_tasks(n_completed=2, provider="anthropic")
        text = PrometheusExporter(c).export()
        assert 'provider="anthropic"' in text
        assert "orxaq_provider_latency_avg_ms" in text
        assert "orxaq_provider_latency_p95_ms" in text
        assert "orxaq_provider_success_rate" in text

    def test_multiple_providers(self) -> None:
        c = MetricsCollector()
        c.record_task(_make_task(task_id="a1", provider="vllm"))
        c.record_task(_make_task(task_id="a2", provider="openai"))
        text = PrometheusExporter(c).export()
        assert 'provider="vllm"' in text
        assert 'provider="openai"' in text

    def test_custom_namespace(self) -> None:
        c = _collector_with_tasks(n_completed=1)
        text = PrometheusExporter(c, namespace="myapp").export()
        assert "myapp_tasks_total" in text
        assert "orxaq_tasks_total" not in text

    def test_uptime_present(self) -> None:
        c = _collector_with_tasks(n_completed=1)
        text = PrometheusExporter(c).export()
        assert "orxaq_uptime_seconds" in text

    def test_tasks_per_minute_present(self) -> None:
        c = _collector_with_tasks(n_completed=1)
        text = PrometheusExporter(c).export()
        assert "orxaq_tasks_per_minute" in text


# ── HealthCheck ──────────────────────────────────────────


class TestHealthCheckHealthy:
    """HealthCheck returns healthy for normal operation."""

    def test_healthy_with_tasks(self) -> None:
        c = _collector_with_tasks(n_completed=10)
        hc = HealthCheck(c)
        result = hc.check()
        assert result["status"] == "healthy"
        assert result["checks"]["error_rate"] == "ok"
        assert result["checks"]["metrics_collector"] == "ok"

    def test_healthy_fresh_start(self) -> None:
        c = MetricsCollector()
        hc = HealthCheck(c)
        result = hc.check()
        # Fresh start with no tasks but < 60s uptime
        assert result["status"] in ("healthy", "degraded")
        assert result["checks"]["metrics_collector"] == "ok"


class TestHealthCheckDegraded:
    """HealthCheck returns degraded for elevated error rates."""

    def test_degraded_high_error_rate(self) -> None:
        c = _collector_with_tasks(n_completed=8, n_failed=2)
        hc = HealthCheck(c)
        result = hc.check()
        assert result["status"] == "degraded"
        assert result["checks"]["error_rate"] == "warning"

    def test_degraded_no_providers(self) -> None:
        c = MetricsCollector()
        # Force uptime > 60s by backdating
        c._start_time = time.time() - 120
        hc = HealthCheck(c)
        result = hc.check()
        assert result["status"] == "degraded"
        assert result["checks"]["provider_count"] == "warning"


class TestHealthCheckUnhealthy:
    """HealthCheck returns unhealthy for critical failures."""

    def test_unhealthy_majority_failures(self) -> None:
        c = _collector_with_tasks(n_completed=2, n_failed=8)
        hc = HealthCheck(c)
        result = hc.check()
        assert result["status"] == "unhealthy"
        assert result["checks"]["error_rate"] == "critical"

    def test_unhealthy_all_failed(self) -> None:
        c = _collector_with_tasks(n_completed=0, n_failed=5)
        hc = HealthCheck(c)
        result = hc.check()
        assert result["status"] == "unhealthy"


class TestHealthCheckFields:
    """HealthCheck response structure."""

    def test_required_fields(self) -> None:
        c = _collector_with_tasks(n_completed=1)
        result = HealthCheck(c).check()
        assert "status" in result
        assert "checks" in result
        assert "uptime_seconds" in result
        assert "total_tasks" in result
        assert "error_rate" in result
        assert "provider_count" in result

    def test_uptime_positive(self) -> None:
        c = MetricsCollector()
        result = HealthCheck(c).check()
        assert result["uptime_seconds"] >= 0


# ── export_metrics ───────────────────────────────────────


class TestExportMetrics:
    """Top-level export_metrics function."""

    def test_prometheus_format(self) -> None:
        c = _collector_with_tasks(n_completed=3)
        text = export_metrics(c, MetricsExportFormat.PROMETHEUS)
        assert "# HELP" in text
        assert "# TYPE" in text
        assert "orxaq_tasks_total 3" in text

    def test_json_format(self) -> None:
        c = _collector_with_tasks(n_completed=2, n_failed=1)
        text = export_metrics(c, MetricsExportFormat.JSON)
        data = json.loads(text)
        assert data["total_tasks"] == 3
        assert data["completed_tasks"] == 2
        assert data["failed_tasks"] == 1
        assert "providers" in data

    def test_json_roundtrip(self) -> None:
        c = _collector_with_tasks(n_completed=1)
        text = export_metrics(c, MetricsExportFormat.JSON)
        data = json.loads(text)
        assert isinstance(data["uptime_seconds"], float)
        assert isinstance(data["total_cost_usd"], float)

    def test_invalid_format_raises(self) -> None:
        c = MetricsCollector()
        with pytest.raises(ValueError, match="Unsupported"):
            export_metrics(c, "invalid")  # type: ignore[arg-type]
