from __future__ import annotations

"""Tests for per-user Kubernetes sync Job adapters."""

import pytest

from atlas_portal import firefly_user_sync, nextcloud_mail_sync, wger_user_sync


def _cronjob_template() -> dict:
    """Build a CronJob payload shaped like the templates used in the cluster."""

    return {
        "spec": {
            "jobTemplate": {
                "spec": {
                    "template": {
                        "spec": {
                            "containers": [
                                {
                                    "name": "worker",
                                    "env": [
                                        {"name": "ONLY_USERNAME", "value": "old"},
                                        {"name": "FIREFLY_USER_EMAIL", "value": "old"},
                                        {"name": "WGER_USERNAME", "value": "old"},
                                    ],
                                }
                            ]
                        }
                    }
                }
            }
        }
    }


@pytest.mark.parametrize(
    ("module", "namespace_attr", "cronjob_attr", "timeout_attr", "args", "expected_env"),
    [
        (
            nextcloud_mail_sync,
            "NEXTCLOUD_NAMESPACE",
            "NEXTCLOUD_MAIL_SYNC_CRONJOB",
            "NEXTCLOUD_MAIL_SYNC_WAIT_TIMEOUT_SEC",
            ("alice",),
            {"ONLY_USERNAME": "alice"},
        ),
        (
            firefly_user_sync,
            "FIREFLY_NAMESPACE",
            "FIREFLY_USER_SYNC_CRONJOB",
            "FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC",
            ("alice", "alice@example.dev", "pw"),
            {"FIREFLY_USER_EMAIL": "alice@example.dev", "FIREFLY_USER_PASSWORD": "pw"},
        ),
        (
            wger_user_sync,
            "WGER_NAMESPACE",
            "WGER_USER_SYNC_CRONJOB",
            "WGER_USER_SYNC_WAIT_TIMEOUT_SEC",
            ("alice", "alice@example.dev", "pw"),
            {"WGER_USERNAME": "alice", "WGER_EMAIL": "alice@example.dev", "WGER_PASSWORD": "pw"},
        ),
    ],
)
def test_user_sync_modules_render_jobs_and_trigger(monkeypatch, module, namespace_attr, cronjob_attr, timeout_attr, args, expected_env) -> None:
    monkeypatch.setattr(module.settings, namespace_attr, "apps")
    monkeypatch.setattr(module.settings, cronjob_attr, "sync-cron")
    monkeypatch.setattr(module.settings, timeout_attr, 0)
    monkeypatch.setattr(module.time, "time", lambda: 1000)

    posted: list[dict] = []

    def fake_get_json(path: str) -> dict:
        if "cronjobs" in path:
            return _cronjob_template()
        return {"status": {"conditions": [{"type": "Complete", "status": "True"}]}}

    def fake_post_json(path: str, payload: dict) -> dict:
        posted.append(payload)
        return {"metadata": {"name": payload["metadata"]["name"]}}

    monkeypatch.setattr(module, "get_json", fake_get_json)
    monkeypatch.setattr(module, "post_json", fake_post_json)

    result = module.trigger(*args, wait=True)

    assert result["status"] in {"ok", "running"}
    env = posted[0]["spec"]["template"]["spec"]["containers"][0]["env"]
    env_map = {item["name"]: item["value"] for item in env}
    for key, value in expected_env.items():
        assert env_map[key] == value
    assert module._job_succeeded({"status": {"succeeded": 1}})
    assert module._job_failed({"status": {"failed": 1}})


@pytest.mark.parametrize(
    ("module", "namespace_attr", "cronjob_attr", "timeout_attr", "args"),
    [
        (
            nextcloud_mail_sync,
            "NEXTCLOUD_NAMESPACE",
            "NEXTCLOUD_MAIL_SYNC_CRONJOB",
            "NEXTCLOUD_MAIL_SYNC_WAIT_TIMEOUT_SEC",
            ("alice",),
        ),
        (
            firefly_user_sync,
            "FIREFLY_NAMESPACE",
            "FIREFLY_USER_SYNC_CRONJOB",
            "FIREFLY_USER_SYNC_WAIT_TIMEOUT_SEC",
            ("alice", "alice@example.dev", "pw"),
        ),
        (
            wger_user_sync,
            "WGER_NAMESPACE",
            "WGER_USER_SYNC_CRONJOB",
            "WGER_USER_SYNC_WAIT_TIMEOUT_SEC",
            ("alice", "alice@example.dev", "pw"),
        ),
    ],
)
def test_user_sync_modules_cover_edge_paths(monkeypatch, module, namespace_attr, cronjob_attr, timeout_attr, args) -> None:
    assert module._safe_name_fragment("!!!") == "user"
    assert module._job_succeeded({"status": {"conditions": [None, {"type": "Complete", "status": "True"}]}})
    assert not module._job_succeeded({"status": {"conditions": [{"type": "Complete", "status": "False"}]}})
    assert module._job_failed({"status": {"conditions": [None, {"type": "Failed", "status": "True"}]}})
    assert not module._job_failed({"status": {"conditions": [{"type": "Failed", "status": "False"}]}})

    cronjob = _cronjob_template()
    container = cronjob["spec"]["jobTemplate"]["spec"]["template"]["spec"]["containers"][0]
    container["env"] = "not-a-list"
    job = module._job_from_cronjob(cronjob, *args)
    assert job["spec"]["template"]["spec"]["containers"][0]["env"]

    monkeypatch.setattr(module.settings, namespace_attr, "apps")
    monkeypatch.setattr(module.settings, cronjob_attr, "sync-cron")
    monkeypatch.setattr(module.settings, timeout_attr, 5)
    monkeypatch.setattr(module.time, "sleep", lambda *_: None)

    with pytest.raises(RuntimeError, match="missing username"):
        module.trigger("", *args[1:])

    if module in {firefly_user_sync, wger_user_sync}:
        with pytest.raises(RuntimeError, match="missing password"):
            module.trigger(args[0], args[1], "")
        monkeypatch.setattr(module.settings, namespace_attr, "")
        with pytest.raises(RuntimeError, match="not configured"):
            module.trigger(*args)
        monkeypatch.setattr(module.settings, namespace_attr, "apps")

    def cron_then_complete(path: str) -> dict:
        if "cronjobs" in path:
            return _cronjob_template()
        return {"status": {"conditions": [{"type": "Complete", "status": "True"}]}}

    monkeypatch.setattr(module, "get_json", cron_then_complete)
    monkeypatch.setattr(module, "post_json", lambda path, payload: {})
    assert module.trigger(*args, wait=False)["status"] == "queued"

    monkeypatch.setattr(module, "post_json", lambda path, payload: {"metadata": {"name": ""}})
    with pytest.raises(RuntimeError, match="job name missing"):
        module.trigger(*args, wait=True)

    monkeypatch.setattr(module, "post_json", lambda path, payload: {"metadata": {"name": payload["metadata"]["name"]}})
    clock = iter([0, 1, 2])
    monkeypatch.setattr(module.time, "time", lambda: next(clock))
    assert module.trigger(*args, wait=True)["status"] == "ok"

    def cron_then_failed(path: str) -> dict:
        if "cronjobs" in path:
            return _cronjob_template()
        return {"status": {"conditions": [{"type": "Failed", "status": "True"}]}}

    clock = iter([0, 1, 2])
    monkeypatch.setattr(module.time, "time", lambda: next(clock))
    monkeypatch.setattr(module, "get_json", cron_then_failed)
    assert module.trigger(*args, wait=True)["status"] == "error"
