"""tests/test_admin.py — Tests for the admin health endpoint version field.

Specifically verifies that the base (compact) /health response includes the
`version` field so external consumers like the `cortex-status` skill can read it
without needing ?detail=true.

Related: PR #758 added version field to health endpoint.
"""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
from unittest.mock import AsyncMock, MagicMock

import pytest

pytest.importorskip("fastapi", reason="fastapi not installed")
pytest.importorskip("httpx", reason="httpx not installed")

from fastapi.testclient import TestClient  # noqa: E402


# ---------------------------------------------------------------------------
# Minimal fake dataclasses mirroring real plugin contracts
# ---------------------------------------------------------------------------

@dataclass
class _FakeHealthReport:
    ok: bool = True
    storage: str = "ok"
    modules: Dict[str, str] = field(default_factory=lambda: {"storage": "ok"})
    errors: List[str] = field(default_factory=list)
    timestamp: str = "2025-01-01T00:00:00+00:00"


def _make_mock_plugin(*, storage_ok: bool = True) -> MagicMock:
    """Build a minimal MagicMock that looks like CortexPlugin."""
    plugin = MagicMock()
    plugin._init_errors = []
    plugin.health = AsyncMock(return_value=_FakeHealthReport())

    cfg = MagicMock()
    cfg.owner_id = "default"
    cfg.quality_gates_enabled = False
    cfg.metrics_enabled = True
    plugin.config = cfg

    if storage_ok:
        storage = MagicMock()
        storage.ping = AsyncMock(return_value=None)
        storage.initialize = AsyncMock(return_value=None)
        storage.close = AsyncMock(return_value=None)
        plugin.storage = storage
    else:
        plugin.storage = None

    metrics = MagicMock()

    async def _get_summary():
        return {
            "error_total": 0.0,
            "request_total": 10.0,
            "latency_p99": 0.1,
        }

    metrics.get_summary = _get_summary
    plugin.metrics = metrics

    return plugin


def _build_client(plugin: MagicMock) -> TestClient:
    """Build a TestClient for the admin router with the given mock plugin."""
    from fastapi import FastAPI
    from cortex.api.routes.admin import router, _make_routes

    app = FastAPI()

    def _noop_verify(x: Any = None):
        return None

    _make_routes(verify_api_key=_noop_verify)
    app.include_router(router)

    # Inject plugin via the shared plugin_holder so get_plugin() resolves
    from cortex.api import deps as _deps
    _deps.plugin_holder["plugin"] = plugin

    return TestClient(app)


# ---------------------------------------------------------------------------
# Tests: version field in health response
# ---------------------------------------------------------------------------

class TestHealthVersionField:
    """Verify that /health always includes the version field."""

    @pytest.fixture
    def client(self):
        plugin = _make_mock_plugin(storage_ok=True)
        return _build_client(plugin)

    def test_compact_health_includes_version(self, client):
        """Base /health response (no ?detail) must include 'version' key."""
        resp = client.get("/api/v1/health")
        assert resp.status_code == 200
        data = resp.json()
        assert "version" in data, (
            "Health response is missing 'version' field — "
            "cortex-status skill cannot read version without it"
        )

    def test_compact_health_version_is_string(self, client):
        """The version field must be a non-empty string."""
        resp = client.get("/api/v1/health")
        data = resp.json()
        assert isinstance(data.get("version"), str)
        assert len(data["version"]) > 0

    def test_compact_health_version_matches_package(self, client):
        """The version field must match the installed cortex package version."""
        from cortex import __version__
        resp = client.get("/api/v1/health")
        data = resp.json()
        assert data["version"] == __version__, (
            f"Expected version={__version__!r}, got {data.get('version')!r}"
        )

    def test_detail_health_also_includes_version(self, client):
        """?detail=true response must also include 'version'."""
        resp = client.get("/api/v1/health?detail=true")
        assert resp.status_code == 200
        data = resp.json()
        assert "version" in data

    def test_health_ok_and_status_present(self, client):
        """Base response must include 'ok' and 'status' fields (regression guard)."""
        resp = client.get("/api/v1/health")
        data = resp.json()
        assert "ok" in data
        assert "status" in data
        assert data["status"] in ("healthy", "degraded", "unhealthy")
