"""Integration tests for ilum-api running against a live minikube cluster.

Run with:
    pytest tests/integration/test_api_live.py -v -s

Requires:
    - kubectl port-forward svc/ilum-api 8080:8080 running in another terminal
    - ILUM_TEST_BASE_URL (default: http://localhost:8080)
    - ILUM_TEST_API_KEY (default: CHANGEMEPLEASE)

Note: Mutation tests (enable/disable) trigger helm upgrades that restart the API
pod. The poll_operation helper handles reconnection automatically.
"""

from __future__ import annotations

import httpx
import pytest

from tests.integration.conftest import (
    _TRANSIENT_ERRORS,
    _restart_port_forward,
    _wait_for_api,
    poll_operation,
)

pytestmark = pytest.mark.integration


def _ensure_api_reachable(auth_client: httpx.Client) -> None:
    """Ensure the API is reachable, reconnecting if needed after a pod restart."""
    try:
        resp = auth_client.get("/health")
        if resp.status_code == 200:
            return
    except _TRANSIENT_ERRORS:
        pass
    base_url = str(auth_client.base_url).rsplit("/api/v1", 1)[0]
    print("  API unreachable, re-establishing port-forward...")
    _restart_port_forward(base_url)
    _wait_for_api(base_url, timeout=180)


# ───────────────────────────────────────────────────────────
# 3a. Health & Connectivity
# ───────────────────────────────────────────────────────────


class TestHealth:
    def test_health_check(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/health")
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "ok"

    def test_health_without_auth(self, unauth_client: httpx.Client) -> None:
        resp = unauth_client.get("/health")
        assert resp.status_code == 200
        assert resp.json()["status"] == "ok"


# ───────────────────────────────────────────────────────────
# 3b. Authentication
# ───────────────────────────────────────────────────────────


class TestAuth:
    def test_no_auth_headers(self, unauth_client: httpx.Client) -> None:
        resp = unauth_client.get("/modules")
        assert resp.status_code == 401

    def test_wrong_api_key(self, base_url: str) -> None:
        client = httpx.Client(
            base_url=f"{base_url}/api/v1",
            headers={"X-API-Key": "wrong-key"},
            timeout=30.0,
        )
        resp = client.get("/modules")
        assert resp.status_code == 401

    def test_valid_api_key(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules")
        assert resp.status_code == 200

    def test_nginx_cookie_auth(self, base_url: str) -> None:
        client = httpx.Client(
            base_url=f"{base_url}/api/v1",
            headers={"X-Ilum-Authenticated": "true"},
            timeout=30.0,
        )
        resp = client.get("/modules")
        assert resp.status_code == 200


# ───────────────────────────────────────────────────────────
# 3c. Status Endpoints
# ───────────────────────────────────────────────────────────


class TestStatus:
    def test_release_info(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/release")
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "ilum"
        assert data["namespace"] == "default"
        assert data["revision"] >= 77

    def test_pod_listing(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/pods")
        assert resp.status_code == 200
        pods = resp.json()
        assert isinstance(pods, list)
        assert len(pods) > 0
        pod_names = [p["name"] for p in pods]
        assert any("ilum-core" in name for name in pod_names)
        assert any("ilum-api" in name for name in pod_names)


# ───────────────────────────────────────────────────────────
# 3d. Module Listing & Detail
# ───────────────────────────────────────────────────────────


class TestModules:
    def test_list_all_modules(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules")
        assert resp.status_code == 200
        modules = resp.json()
        assert len(modules) >= 30
        names = {m["name"] for m in modules}
        assert "core" in names
        assert "streamlit" in names

    def test_filter_by_category(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules", params={"category": "notebook"})
        assert resp.status_code == 200
        modules = resp.json()
        assert len(modules) > 0
        assert all(m["category"] == "notebook" for m in modules)

    def test_invalid_category(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules", params={"category": "bogus"})
        assert resp.status_code == 400

    def test_filter_enabled(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules", params={"enabled": "true"})
        assert resp.status_code == 200
        modules = resp.json()
        assert all(m["enabled"] is True for m in modules)

    def test_filter_disabled(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules", params={"enabled": "false"})
        assert resp.status_code == 200
        modules = resp.json()
        assert all(m["enabled"] is False for m in modules)

    def test_module_detail_core(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules/core")
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "core"
        assert data["required"] is True
        assert isinstance(data["pods"], list)

    def test_module_detail_streamlit(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules/streamlit")
        assert resp.status_code == 200
        data = resp.json()
        assert data["name"] == "streamlit"
        assert data["category"] == "analytics"

    def test_module_not_found(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/modules/nonexistent")
        assert resp.status_code == 400


# ───────────────────────────────────────────────────────────
# 3e. Values Endpoints (Read-Only)
# ───────────────────────────────────────────────────────────


class TestValues:
    def test_get_current_values(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, dict)
        assert "ilum-core" in data

    def test_values_diff(self, auth_client: httpx.Client) -> None:
        resp = auth_client.get("/values/diff")
        assert resp.status_code == 200
        data = resp.json()
        assert "has_drift" in data
        assert "snapshot_exists" in data
        assert "changes" in data


# ───────────────────────────────────────────────────────────
# 3f. Module Enable/Disable (Mutations)
# ───────────────────────────────────────────────────────────


class TestModuleMutations:
    def test_disable_required_module(self, auth_client: httpx.Client) -> None:
        resp = auth_client.post("/modules/core/disable")
        assert resp.status_code == 400
        data = resp.json()
        assert "required" in data["detail"].lower()

    def test_enable_nonexistent_module(self, auth_client: httpx.Client) -> None:
        resp = auth_client.post("/modules/nonexistent/enable")
        assert resp.status_code == 400

    def test_enable_streamlit(self, auth_client: httpx.Client) -> None:
        resp = auth_client.post("/modules/streamlit/enable")
        assert resp.status_code == 202
        data = resp.json()
        assert "id" in data
        op_id = data["id"]

        result = poll_operation(auth_client, op_id)
        assert result["status"] == "completed", f"Enable failed: {result.get('error', '')}"

    def test_verify_streamlit_enabled(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.get("/modules/streamlit")
        assert resp.status_code == 200
        assert resp.json()["enabled"] is True

    def test_disable_streamlit(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.post("/modules/streamlit/disable")
        assert resp.status_code == 202
        data = resp.json()
        assert "id" in data
        op_id = data["id"]

        result = poll_operation(auth_client, op_id)
        assert result["status"] == "completed", f"Disable failed: {result.get('error', '')}"

    def test_verify_streamlit_disabled(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.get("/modules/streamlit")
        assert resp.status_code == 200
        assert resp.json()["enabled"] is False


# ───────────────────────────────────────────────────────────
# 3g. Operations Tracking
# ───────────────────────────────────────────────────────────


class TestOperations:
    def test_list_operations(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.get("/operations")
        assert resp.status_code == 200
        ops = resp.json()
        assert isinstance(ops, list)

    def test_get_specific_operation(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.get("/operations")
        assert resp.status_code == 200
        ops = resp.json()
        if len(ops) == 0:
            pytest.skip("No operations to inspect (pod may have restarted)")

        op_id = ops[0]["id"]
        resp = auth_client.get(f"/operations/{op_id}")
        assert resp.status_code == 200
        data = resp.json()
        assert data["id"] == op_id
        assert data["status"] in ("pending", "running", "completed", "failed")

    def test_unknown_operation(self, auth_client: httpx.Client) -> None:
        _ensure_api_reachable(auth_client)
        resp = auth_client.get("/operations/nonexistent")
        assert resp.status_code == 404
