"""Integration tests for full module lifecycle via ilum-api.

Exercises enable→verify→disable→verify cycles for every module category,
dependency chains, conflict detection, multi-flag modules, and CRD pre-install.

Run with:
    pytest tests/integration/test_module_lifecycle.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)

Estimated runtime: 60–90 minutes (each helm upgrade cycle takes ~2–5 min).

The test suite restores the cluster to its pre-test state on completion.
"""

from __future__ import annotations

import httpx
import pytest

from tests.integration.conftest import (
    disable_module,
    enable_module,
    ensure_api_reachable,
    poll_operation,
    verify_module_disabled,
    verify_module_enabled,
)

pytestmark = pytest.mark.integration

# Modules that should be enabled before and after the test suite
ORIGINALLY_ENABLED = frozenset(
    {
        "core",
        "ui",
        "mongodb",
        "postgresql",
        "minio",
        "jupyter",
        "gitea",
        "sql",
        "hive-metastore",
        "marquez",
        "api",
    }
)


# ───────────────────────────────────────────────────────────
# 3a. Standalone Modules (No Dependencies)
# ───────────────────────────────────────────────────────────


class TestStandaloneModules:
    """Enable/disable modules with zero `requires`."""

    def test_enable_streamlit(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "streamlit")

    def test_verify_streamlit_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "streamlit", expect_pods=False)

    def test_disable_streamlit(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "streamlit")

    def test_verify_streamlit_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "streamlit")

    # minio — currently enabled, disable then re-enable
    def test_disable_minio(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "minio")

    def test_verify_minio_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "minio")

    def test_reenable_minio(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "minio")

    def test_verify_minio_reenabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "minio")

    # openldap
    def test_enable_openldap(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "openldap")

    def test_verify_openldap_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "openldap", expect_pods=False)

    def test_disable_openldap(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "openldap")

    def test_verify_openldap_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "openldap")

    # graphite
    def test_enable_graphite(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "graphite")

    def test_verify_graphite_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "graphite", expect_pods=False)

    def test_disable_graphite(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "graphite")

    def test_verify_graphite_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "graphite")

    # clickhouse
    def test_enable_clickhouse(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "clickhouse")

    def test_verify_clickhouse_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "clickhouse", expect_pods=False)

    def test_disable_clickhouse(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "clickhouse")

    def test_verify_clickhouse_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "clickhouse")

    # mlflow
    def test_enable_mlflow(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "mlflow")

    def test_verify_mlflow_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "mlflow", expect_pods=False)

    def test_disable_mlflow(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "mlflow")

    def test_verify_mlflow_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "mlflow")


# ───────────────────────────────────────────────────────────
# 3b. Modules With Dependencies (postgresql required)
# ───────────────────────────────────────────────────────────


class TestDependentModules:
    """Enable/disable modules that require postgresql (already enabled)."""

    # airflow
    def test_enable_airflow(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "airflow")

    def test_verify_airflow_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "airflow", expect_pods=False)

    def test_disable_airflow(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "airflow")

    def test_verify_airflow_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "airflow")

    # superset
    def test_enable_superset(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "superset")

    def test_verify_superset_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "superset", expect_pods=False)

    def test_disable_superset(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "superset")

    def test_verify_superset_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "superset")

    # kestra
    def test_enable_kestra(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "kestra")

    def test_verify_kestra_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "kestra", expect_pods=False)

    def test_disable_kestra(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "kestra")

    def test_verify_kestra_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "kestra")

    # n8n (no deps)
    def test_enable_n8n(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "n8n")

    def test_verify_n8n_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "n8n", expect_pods=False)

    def test_disable_n8n(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "n8n")

    def test_verify_n8n_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "n8n")

    # mageai
    def test_enable_mageai(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "mageai")

    def test_verify_mageai_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "mageai", expect_pods=False)

    def test_disable_mageai(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "mageai")

    def test_verify_mageai_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "mageai")

    # unity-catalog
    def test_enable_unity_catalog(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "unity-catalog")

    def test_verify_unity_catalog_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "unity-catalog", expect_pods=False)

    def test_disable_unity_catalog(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "unity-catalog")

    def test_verify_unity_catalog_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "unity-catalog")


# ───────────────────────────────────────────────────────────
# 3c. Modules Requiring Core
# ───────────────────────────────────────────────────────────


class TestCoreDepModules:
    """Enable/disable modules whose dependency chain includes `core`."""

    # jupyter — currently enabled, disable then re-enable
    def test_disable_jupyter(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "jupyter")

    def test_verify_jupyter_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "jupyter")

    def test_reenable_jupyter(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "jupyter")

    def test_verify_jupyter_reenabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "jupyter")

    # zeppelin
    def test_enable_zeppelin(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "zeppelin")

    def test_verify_zeppelin_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "zeppelin", expect_pods=False)

    def test_disable_zeppelin(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "zeppelin")

    def test_verify_zeppelin_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "zeppelin")

    # jupyterhub
    def test_enable_jupyterhub(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "jupyterhub")

    def test_verify_jupyterhub_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "jupyterhub", expect_pods=False)

    def test_disable_jupyterhub(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "jupyterhub")

    def test_verify_jupyterhub_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "jupyterhub")

    # livy-proxy
    def test_enable_livy_proxy(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "livy-proxy")

    def test_verify_livy_proxy_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "livy-proxy", expect_pods=False)

    def test_disable_livy_proxy(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "livy-proxy")

    def test_verify_livy_proxy_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "livy-proxy")


# ───────────────────────────────────────────────────────────
# 3d. Multi-Flag Modules
# ───────────────────────────────────────────────────────────


class TestMultiFlagModules:
    """Modules that set multiple Helm values beyond `<chart>.enabled`."""

    # kafka — sets ilum-core.communication.type=kafka
    # NOTE: Bitnami Kafka chart requires existing SASL passwords on upgrade
    # (sasl.interbroker.password). This is a chart limitation, not a test bug.
    @pytest.mark.xfail(reason="Bitnami Kafka requires SASL passwords on upgrade")
    def test_enable_kafka(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "kafka")

    @pytest.mark.xfail(reason="Bitnami Kafka requires SASL passwords on upgrade")
    def test_verify_kafka_enabled_and_values(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "kafka", expect_pods=False)
        # Verify kafka-specific values were set
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        values = resp.json()
        core_values = values.get("ilum-core", {})
        comm = core_values.get("communication", {})
        assert comm.get("type") == "kafka", (
            f"Expected communication.type=kafka, got {comm.get('type')}"
        )

    def test_disable_kafka(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "kafka")

    def test_verify_kafka_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "kafka")

    # loki — sets global.logAggregation.enabled, .loki.enabled, .promtail.enabled
    def test_enable_loki(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "loki")

    def test_verify_loki_enabled_and_values(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "loki", expect_pods=False)
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        values = resp.json()
        log_agg = values.get("global", {}).get("logAggregation", {})
        assert log_agg.get("enabled") is True, "logAggregation.enabled should be true"
        assert log_agg.get("loki", {}).get("enabled") is True, "loki.enabled should be true"
        assert log_agg.get("promtail", {}).get("enabled") is True, "promtail.enabled should be true"

    def test_disable_loki(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "loki")

    def test_verify_loki_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "loki")

    # sql — already enabled, read-only verification
    def test_verify_sql_detail(self, auth_client: httpx.Client) -> None:
        data = verify_module_enabled(auth_client, "sql")
        assert data["category"] == "sql"

    def test_verify_sql_values(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        values = resp.json()
        core_values = values.get("ilum-core", {})
        sql_config = core_values.get("sql", {})
        assert sql_config.get("enabled") is True, "ilum-core.sql.enabled should be true"

    # hive-metastore — already enabled, read-only verification
    def test_verify_hive_metastore_detail(self, auth_client: httpx.Client) -> None:
        data = verify_module_enabled(auth_client, "hive-metastore")
        assert data["category"] == "sql"

    def test_verify_hive_metastore_values(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        values = resp.json()
        core_values = values.get("ilum-core", {})
        metastore = core_values.get("metastore", {})
        assert metastore.get("enabled") is True, "metastore.enabled should be true"
        assert metastore.get("type") == "hive", (
            f"Expected metastore.type=hive, got {metastore.get('type')}"
        )


# ───────────────────────────────────────────────────────────
# 3e. Conflict Testing (hive-metastore ↔ nessie)
# ───────────────────────────────────────────────────────────


class TestConflicts:
    """Test mutual exclusion between hive-metastore and nessie.

    The API does not block conflicts synchronously (it returns 202 for any valid
    module enable).  The conflict is recorded as a warning in the ReleasePlan but
    the operation proceeds.  We test the *clean* flow: disable the active one
    first, then enable the other.
    """

    def test_disable_hive_metastore(self, auth_client: httpx.Client) -> None:
        """Disable hive-metastore to make room for nessie."""
        disable_module(auth_client, "hive-metastore")

    def test_verify_hive_metastore_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "hive-metastore")

    def test_enable_nessie(self, auth_client: httpx.Client) -> None:
        """Enable nessie now that hive-metastore is off."""
        enable_module(auth_client, "nessie")

    def test_verify_nessie_enabled(self, auth_client: httpx.Client) -> None:
        data = verify_module_enabled(auth_client, "nessie", expect_pods=False)
        assert "hive-metastore" in data.get("conflicts_with", [])

    def test_verify_nessie_values(self, auth_client: httpx.Client) -> None:
        """Verify metastore.type switched to nessie."""
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/values")
        assert resp.status_code == 200
        values = resp.json()
        metastore = values.get("ilum-core", {}).get("metastore", {})
        assert metastore.get("type") == "nessie", (
            f"Expected metastore.type=nessie, got {metastore.get('type')}"
        )

    def test_hive_metastore_conflicts_while_nessie_active(self, auth_client: httpx.Client) -> None:
        """Enabling hive-metastore while nessie is active succeeds at the API
        level (no sync conflict check) but creates inconsistent state.
        We verify the API accepts the request — conflict handling is advisory.
        """
        ensure_api_reachable(auth_client)
        resp = auth_client.post("/modules/hive-metastore/enable")
        # API accepts the request (no sync conflict check)
        assert resp.status_code == 202
        op_id = resp.json()["id"]
        result = poll_operation(auth_client, op_id)
        # The operation completes (helm upgrade runs regardless of warnings)
        assert result["status"] in ("completed", "failed")
        if result["status"] == "completed":
            # Clean up: both are now enabled — disable hive-metastore
            disable_module(auth_client, "hive-metastore")

    def test_disable_nessie(self, auth_client: httpx.Client) -> None:
        """Disable nessie to restore original state."""
        disable_module(auth_client, "nessie")

    def test_verify_nessie_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "nessie")

    def test_restore_hive_metastore(self, auth_client: httpx.Client) -> None:
        """Re-enable hive-metastore (original state)."""
        enable_module(auth_client, "hive-metastore")

    def test_verify_hive_metastore_restored(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "hive-metastore", expect_pods=False)


# ───────────────────────────────────────────────────────────
# 3f. CRD-Dependent Module (monitoring)
# ───────────────────────────────────────────────────────────


class TestCRDModule:
    """The monitoring module requires CRDs from prometheus-operator-crds.

    The enable endpoint calls ensure_module_crds() which installs the CRD
    chart before the main helm upgrade.

    NOTE: kube-prometheus-stack is very large and its helm upgrade can leave
    the release stuck in pending-upgrade on resource-constrained clusters
    (e.g., minikube).  These tests are marked xfail to avoid blocking the
    rest of the suite.
    """

    @pytest.mark.xfail(reason="kube-prometheus-stack upgrade may stall on minikube")
    def test_enable_monitoring(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "monitoring")

    @pytest.mark.xfail(reason="kube-prometheus-stack upgrade may stall on minikube")
    def test_verify_monitoring_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "monitoring", expect_pods=False)

    @pytest.mark.xfail(reason="kube-prometheus-stack upgrade may stall on minikube")
    def test_disable_monitoring(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "monitoring")

    @pytest.mark.xfail(reason="kube-prometheus-stack upgrade may stall on minikube")
    def test_verify_monitoring_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "monitoring")


# ───────────────────────────────────────────────────────────
# 3g. Deep Dependency Chain (langfuse → postgresql + clickhouse)
# ───────────────────────────────────────────────────────────


class TestDeepDependencyChain:
    """Langfuse requires both postgresql (already on) and clickhouse (off).

    The resolver should auto-enable clickhouse as a transitive dependency.
    """

    def test_enable_langfuse(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "langfuse")

    def test_verify_langfuse_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "langfuse", expect_pods=False)

    def test_verify_clickhouse_auto_enabled(self, auth_client: httpx.Client) -> None:
        """clickhouse should have been auto-enabled by the dependency resolver."""
        verify_module_enabled(auth_client, "clickhouse", expect_pods=False)

    def test_disable_langfuse(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "langfuse")

    def test_verify_langfuse_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "langfuse")

    def test_cleanup_clickhouse(self, auth_client: httpx.Client) -> None:
        """Disable clickhouse if it's still enabled after langfuse disable."""
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/modules/clickhouse")
        assert resp.status_code == 200
        if resp.json()["enabled"]:
            disable_module(auth_client, "clickhouse")
            verify_module_disabled(auth_client, "clickhouse")


# ───────────────────────────────────────────────────────────
# 3h. Trino (requires hive-metastore chain)
# ───────────────────────────────────────────────────────────


class TestTrinoChain:
    """Trino requires hive-metastore, which should be re-enabled from TestConflicts."""

    def test_precondition_hive_metastore_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "hive-metastore", expect_pods=False)

    def test_enable_trino(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "trino")

    def test_verify_trino_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "trino", expect_pods=False)

    def test_disable_trino(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "trino")

    def test_verify_trino_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "trino")


# ───────────────────────────────────────────────────────────
# 3i. NiFi (standalone orchestration)
# ───────────────────────────────────────────────────────────


class TestNifi:
    def test_enable_nifi(self, auth_client: httpx.Client) -> None:
        enable_module(auth_client, "nifi")

    def test_verify_nifi_enabled(self, auth_client: httpx.Client) -> None:
        verify_module_enabled(auth_client, "nifi", expect_pods=False)

    def test_disable_nifi(self, auth_client: httpx.Client) -> None:
        disable_module(auth_client, "nifi")

    def test_verify_nifi_disabled(self, auth_client: httpx.Client) -> None:
        verify_module_disabled(auth_client, "nifi")


# ───────────────────────────────────────────────────────────
# 3j. Error Cases
# ───────────────────────────────────────────────────────────


class TestErrorCases:
    """Validate error handling for invalid operations."""

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

    def test_disable_required_module_ui(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        resp = auth_client.post("/modules/ui/disable")
        assert resp.status_code == 400
        assert "required" in resp.json()["detail"].lower()

    def test_disable_required_module_mongodb(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        resp = auth_client.post("/modules/mongodb/disable")
        assert resp.status_code == 400
        assert "required" in resp.json()["detail"].lower()

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

    def test_disable_nonexistent_module(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        resp = auth_client.post("/modules/does-not-exist/disable")
        assert resp.status_code == 400

    def test_enable_already_enabled_module(self, auth_client: httpx.Client) -> None:
        """Enabling an already-enabled module should be idempotent."""
        # Use the retry-aware helper which handles helm contention
        enable_module(auth_client, "core")


# ───────────────────────────────────────────────────────────
# 3k. Values Verification
# ───────────────────────────────────────────────────────────


class TestFinalValuesVerification:
    """After all mutations, verify values endpoints and final module state."""

    def test_get_values(self, auth_client: httpx.Client) -> None:
        ensure_api_reachable(auth_client)
        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:
        ensure_api_reachable(auth_client)
        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

    def test_originally_enabled_modules_still_enabled(self, auth_client: httpx.Client) -> None:
        """Verify all modules that were enabled before tests are still enabled."""
        ensure_api_reachable(auth_client)
        resp = auth_client.get("/modules", params={"enabled": "true"})
        assert resp.status_code == 200
        enabled_names = {m["name"] for m in resp.json()}
        for mod_name in ORIGINALLY_ENABLED:
            assert mod_name in enabled_names, (
                f"{mod_name} should still be enabled but is not. "
                f"Currently enabled: {sorted(enabled_names)}"
            )


# ───────────────────────────────────────────────────────────
# 3l. Operations History
# ───────────────────────────────────────────────────────────


class TestOperationsHistory:
    """Verify operations tracking after many mutations."""

    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)
        # After many enable/disable cycles there should be several operations
        # (unless the pod restarted and the in-memory store was cleared)
        if len(ops) > 0:
            print(f"  Found {len(ops)} operations in history")

    def test_latest_operation_has_module_name(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 in store (pod may have restarted)")
        latest = ops[0]
        assert "modules" in latest
        assert isinstance(latest["modules"], list)

    def test_operations_have_timestamps(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 in store (pod may have restarted)")
        for op in ops:
            assert op.get("created_at"), f"Operation {op['id']} missing created_at"
            if op["status"] in ("completed", "failed"):
                assert op.get("completed_at"), (
                    f"Operation {op['id']} is {op['status']} but missing completed_at"
                )
