"""Tests für den Admin-Einstellungen-Tab (Provider & Sprache + Laufzeit-Config).

Getestet: GET /api/admin/config, PUT /api/admin/config/{key},
          DELETE /api/admin/config/{key} — Auth, Custom-Values, Persistenz.
"""

import pytest
from fastapi.testclient import TestClient

import app.dependencies as deps
from app.main import app
from app.config import settings
from app.runtime_config import RUNTIME_SETTABLE, invalidate_cache

client = TestClient(app)
ADMIN = "test-admin-key"
ADM_HDR = {"X-Admin-Key": ADMIN}

PROVIDER_KEYS = [
    "default_stt_provider",
    "default_llm_provider",
    "default_tts_provider",
    "default_language",
]


@pytest.fixture(autouse=True)
def _setup(monkeypatch):
    monkeypatch.setattr(settings, "admin_api_key", ADMIN)
    monkeypatch.setattr(settings, "auth_enabled", False)
    invalidate_cache()
    yield
    invalidate_cache()


# ── GET /api/admin/config ────────────────────────────────────────────────────

def test_get_config_returns_all_keys():
    resp = client.get("/api/admin/config", headers=ADM_HDR)
    assert resp.status_code == 200
    keys = {e["key"] for e in resp.json()}
    assert keys == set(RUNTIME_SETTABLE.keys())


def test_get_config_requires_admin_key():
    resp = client.get("/api/admin/config")
    assert resp.status_code == 401


def test_get_config_provider_keys_present():
    resp = client.get("/api/admin/config", headers=ADM_HDR)
    keys = {e["key"] for e in resp.json()}
    for k in PROVIDER_KEYS:
        assert k in keys, f"Schlüssel {k!r} fehlt in /api/admin/config"


def test_get_config_entry_structure():
    resp = client.get("/api/admin/config", headers=ADM_HDR)
    entry = next(e for e in resp.json() if e["key"] == "default_stt_provider")
    assert "label" in entry
    assert "hint" in entry
    assert "effective_value" in entry
    assert "base_value" in entry
    assert "is_overridden" in entry
    assert entry["is_overridden"] is False


def test_get_config_not_overridden_initially():
    resp = client.get("/api/admin/config", headers=ADM_HDR)
    for entry in resp.json():
        if entry["key"] in PROVIDER_KEYS:
            assert not entry["is_overridden"], (
                f"{entry['key']} sollte initial nicht überschrieben sein"
            )


# ── PUT /api/admin/config/{key} ──────────────────────────────────────────────

@pytest.mark.parametrize("key,value", [
    ("default_stt_provider",  "openrouter"),
    ("default_stt_provider",  "faster-whisper"),
    ("default_llm_provider",  "openrouter"),
    ("default_llm_provider",  "local-openai-compatible"),
    ("default_tts_provider",  "openrouter"),
    ("default_tts_provider",  "piper"),
    ("default_tts_provider",  "chatterbox"),
    ("default_language",      "de"),
    ("default_language",      "en"),
])
def test_put_known_value_accepted(key, value):
    resp = client.put(f"/api/admin/config/{key}", headers=ADM_HDR, json={"value": value})
    assert resp.status_code == 200
    assert resp.json()["value"] == value


@pytest.mark.parametrize("key,custom_value", [
    ("default_stt_provider", "my-custom-stt"),
    ("default_llm_provider", "local-openai-compatible-v2"),
    ("default_tts_provider", "future-tts-engine"),
    ("default_language",     "pl"),
])
def test_put_custom_value_accepted(key, custom_value):
    """Eigener Wert (nicht in der Dropdown-Liste) muss akzeptiert werden."""
    resp = client.put(f"/api/admin/config/{key}", headers=ADM_HDR, json={"value": custom_value})
    assert resp.status_code == 200
    assert resp.json()["value"] == custom_value


def test_put_custom_value_reflected_in_get():
    """Gespeicherter Custom-Wert erscheint in effective_value."""
    client.put("/api/admin/config/default_stt_provider", headers=ADM_HDR,
               json={"value": "my-new-stt"})
    invalidate_cache()
    resp = client.get("/api/admin/config", headers=ADM_HDR)
    entry = next(e for e in resp.json() if e["key"] == "default_stt_provider")
    assert entry["effective_value"] == "my-new-stt"
    assert entry["is_overridden"] is True


def test_put_unknown_key_returns_400():
    resp = client.put("/api/admin/config/nonexistent_key", headers=ADM_HDR,
                      json={"value": "x"})
    assert resp.status_code == 400


def test_put_trims_whitespace():
    resp = client.put("/api/admin/config/default_stt_provider", headers=ADM_HDR,
                      json={"value": "  openrouter  "})
    assert resp.status_code == 200
    assert resp.json()["value"] == "openrouter"


def test_put_requires_admin_key():
    resp = client.put("/api/admin/config/default_stt_provider", json={"value": "x"})
    assert resp.status_code == 401


# ── DELETE /api/admin/config/{key} ──────────────────────────────────────────

def test_delete_removes_override():
    client.put("/api/admin/config/default_tts_provider", headers=ADM_HDR,
               json={"value": "future-engine"})
    invalidate_cache()

    del_resp = client.delete("/api/admin/config/default_tts_provider", headers=ADM_HDR)
    assert del_resp.status_code == 200
    assert del_resp.json()["deleted"] == "default_tts_provider"

    invalidate_cache()
    cfg = {e["key"]: e for e in
           client.get("/api/admin/config", headers=ADM_HDR).json()}
    assert not cfg["default_tts_provider"]["is_overridden"]


def test_delete_nonexistent_override_returns_404():
    resp = client.delete("/api/admin/config/default_tts_provider", headers=ADM_HDR)
    assert resp.status_code == 404


def test_delete_unknown_key_returns_400():
    resp = client.delete("/api/admin/config/does_not_exist", headers=ADM_HDR)
    assert resp.status_code == 400


def test_delete_requires_admin_key():
    resp = client.delete("/api/admin/config/default_tts_provider")
    assert resp.status_code == 401


# ── Persistenz: Override wirkt sich auf Runtime aus ──────────────────────────

def test_override_reflected_in_runtime_settings():
    from app.runtime_config import runtime_settings
    client.put("/api/admin/config/default_language", headers=ADM_HDR,
               json={"value": "fr"})
    invalidate_cache()
    assert runtime_settings.default_language == "fr"


def test_delete_restores_base_value():
    from app.runtime_config import runtime_settings
    base = str(settings.default_language)

    client.put("/api/admin/config/default_language", headers=ADM_HDR,
               json={"value": "it"})
    invalidate_cache()
    assert runtime_settings.default_language == "it"

    client.delete("/api/admin/config/default_language", headers=ADM_HDR)
    invalidate_cache()
    assert str(runtime_settings.default_language) == base


def test_admin_sets_user_allowed_languages():
    created = client.post("/api/admin/users", headers=ADM_HDR,
                          json={"display_name": "Oma"}).json()
    uid = created["user_id"]
    resp = client.put(f"/api/admin/users/{uid}/prefs", headers=ADM_HDR,
                      json={"allowed_languages": "de,en"})
    assert resp.status_code == 200
    assert resp.json()["prefs"]["allowed_languages"] == "de,en"
    # taucht in der Nutzerliste auf
    users = client.get("/api/admin/users", headers=ADM_HDR).json()
    entry = next(u for u in users if u["user_id"] == uid)
    assert entry["allowed_languages"] == "de,en"


def test_admin_can_flag_user_as_admin():
    created = client.post("/api/admin/users", headers=ADM_HDR,
                          json={"display_name": "Caregiver"}).json()
    uid = created["user_id"]
    users = client.get("/api/admin/users", headers=ADM_HDR).json()
    assert next(u for u in users if u["user_id"] == uid)["is_admin"] is False
    r = client.put(f"/api/admin/users/{uid}/admin", headers=ADM_HDR, json={"is_admin": True})
    assert r.status_code == 200 and r.json()["is_admin"] is True
    users = client.get("/api/admin/users", headers=ADM_HDR).json()
    assert next(u for u in users if u["user_id"] == uid)["is_admin"] is True


def test_openrouter_models_endpoint_returns_sorted_ids(monkeypatch):
    import app.api.admin as adm
    import httpx
    adm._OR_MODELS_CACHE.update(ts=0.0, ids=[])

    class FakeResp:
        def raise_for_status(self):
            pass
        def json(self):
            return {"data": [{"id": "b/model"}, {"id": "a/model"}, {"x": 1}]}

    async def fake_get(self, url, headers=None):
        return FakeResp()

    monkeypatch.setattr(httpx.AsyncClient, "get", fake_get)
    r = client.get("/api/admin/openrouter/models", headers=ADM_HDR)
    assert r.status_code == 200
    assert r.json()["models"] == ["a/model", "b/model"]
