"""Tests for the config schema and module config endpoints."""

from __future__ import annotations

from unittest.mock import MagicMock

from fastapi.testclient import TestClient


class TestConfigSchemaEndpoint:
    def test_get_schema_for_known_module(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "ilum-jupyter": {
                "enabled": True,
                "replicaCount": 1,
                "image": {"tag": "6.7.0", "repository": "ilum/jupyter"},
                "resources": {
                    "requests": {"memory": "2Gi", "cpu": "1"},
                    "limits": {"memory": "4Gi", "cpu": "2"},
                },
            },
        }
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        data = resp.json()
        assert data["module_name"] == "jupyter"
        assert data["values_key"] == "ilum-jupyter"
        assert len(data["tabs"]) > 0
        # Flatten all fields across tabs
        all_fields = [f for tab in data["tabs"] for f in tab["fields"]]
        paths = [f["path"] for f in all_fields]
        assert "replicaCount" in paths

    def test_tabs_structure(self, api_client: TestClient, mock_api_manager: MagicMock) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "ilum-jupyter": {
                "replicaCount": 1,
                "resources": {"requests": {"memory": "2Gi"}},
            },
        }
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        data = resp.json()
        for tab in data["tabs"]:
            assert "name" in tab
            assert "label" in tab
            assert "fields" in tab
            assert isinstance(tab["fields"], list)

    def test_tabs_order(self, api_client: TestClient, mock_api_manager: MagicMock) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "ilum-jupyter": {
                "enabled": True,
                "replicaCount": 1,
                "image": {"tag": "6.7.0"},
                "resources": {"requests": {"memory": "2Gi"}},
                "persistence": {"size": "10Gi"},
                "customOption": "value",
            },
        }
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        tab_names = [t["name"] for t in resp.json()["tabs"]]
        expected_order = ["general", "resources", "storage", "advanced"]
        # Tabs should appear in this order (some may be absent)
        filtered = [t for t in expected_order if t in tab_names]
        assert tab_names == filtered

    def test_field_has_tab_and_current_value(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "ilum-jupyter": {
                "replicaCount": 3,
                "resources": {"requests": {"memory": "4Gi"}},
            },
        }
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        all_fields = [f for tab in resp.json()["tabs"] for f in tab["fields"]]
        replica_field = next((f for f in all_fields if f["path"] == "replicaCount"), None)
        assert replica_field is not None
        assert "tab" in replica_field
        assert "current_value" in replica_field
        assert replica_field["current_value"] == 3

    def test_empty_schema_for_module_without_config(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {}
        resp = api_client.get("/api/v1/modules/core/config-schema")
        assert resp.status_code == 200
        data = resp.json()
        assert data["module_name"] == "core"
        assert data["tabs"] == []

    def test_unknown_module_returns_400(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        resp = api_client.get("/api/v1/modules/nonexistent/config-schema")
        assert resp.status_code == 400

    def test_minio_has_persistence_field(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "minio": {"persistence": {"size": "10Gi"}},
        }
        resp = api_client.get("/api/v1/modules/minio/config-schema")
        assert resp.status_code == 200
        all_fields = [f for tab in resp.json()["tabs"] for f in tab["fields"]]
        paths = [f["path"] for f in all_fields]
        assert "persistence.size" in paths

    def test_select_field_has_options(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {
            "ilum-jupyter": {
                "resources": {"requests": {"memory": "2Gi"}},
            },
        }
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        all_fields = [f for tab in resp.json()["tabs"] for f in tab["fields"]]
        memory_field = next(f for f in all_fields if f["path"] == "resources.requests.memory")
        assert memory_field["field_type"] == "select"
        assert len(memory_field["options"]) > 0

    def test_values_key_in_response(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.return_value = {}
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        assert resp.json()["values_key"] == "ilum-jupyter"

    def test_fallback_when_computed_values_fail(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        mock_api_manager.fetch_computed_values.side_effect = Exception("cluster error")
        resp = api_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 200
        data = resp.json()
        assert data["module_name"] == "jupyter"
        # Should still return registry fields even when computed values fail
        all_fields = [f for tab in data["tabs"] for f in tab["fields"]]
        paths = [f["path"] for f in all_fields]
        assert "replicaCount" in paths

    def test_auth_required(self, unauthed_client: TestClient, mock_api_manager: MagicMock) -> None:
        resp = unauthed_client.get("/api/v1/modules/jupyter/config-schema")
        assert resp.status_code == 401


class TestModuleConfigUpdate:
    def test_update_returns_202(self, api_client: TestClient, mock_api_manager: MagicMock) -> None:
        resp = api_client.put(
            "/api/v1/modules/jupyter/config",
            json={"values": {"image.tag": "6.8.0"}},
        )
        assert resp.status_code == 202
        data = resp.json()
        assert "id" in data
        assert data["status"] == "running"

    def test_update_unknown_module_returns_400(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        resp = api_client.put(
            "/api/v1/modules/nonexistent/config",
            json={"values": {"image.tag": "6.8.0"}},
        )
        assert resp.status_code == 400

    def test_update_empty_values_returns_400(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        resp = api_client.put(
            "/api/v1/modules/jupyter/config",
            json={"values": {}},
        )
        assert resp.status_code == 400

    def test_update_auth_required(
        self, unauthed_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        resp = unauthed_client.put(
            "/api/v1/modules/jupyter/config",
            json={"values": {"image.tag": "6.8.0"}},
        )
        assert resp.status_code == 401

    def test_update_message_includes_module_name(
        self, api_client: TestClient, mock_api_manager: MagicMock
    ) -> None:
        resp = api_client.put(
            "/api/v1/modules/jupyter/config",
            json={"values": {"replicaCount": 2}},
        )
        assert resp.status_code == 202
        assert "jupyter" in resp.json()["message"]
