"""E2E: config set/get/unset lifecycle with env verification.

Deploys an app, sets config vars, and verifies they appear as
environment variables inside the running pod. Also verifies
--secret stores in K8s Secret (not ConfigMap).

Run with: pytest tests/e2e/test_config_lifecycle.py -v -s
"""

from __future__ import annotations

from typing import Any

import pytest

from kuberoku.factory import KuberokuFactory
from kuberoku.k8s import labels as k8s_labels
from kuberoku.k8s.resources import safe_name
from tests.e2e.helpers import (
    cleanup_namespace_fixture,
    exec_in_pod_safe,
    find_running_pod,
    is_pod_ready,
    make_real_factory,
    poll_until,
    skip_if_no_cluster,
    wait_for_pods_ready,
)


def _wait_for_env_in_pod(
    factory: KuberokuFactory,
    app: str,
    env_key: str,
    env_value: str,
    timeout: int = 120,
) -> None:
    """Poll until a running pod has the expected env var.

    config.set triggers a rolling restart — old pods may linger.
    This waits until a pod with the new env is actually Running.
    """
    selector = k8s_labels.selector(factory.domain, app, "web")

    def _check() -> bool:
        pods = factory.k8s.list_pods(factory.namespace, labels=selector)
        for pod in pods:
            if not is_pod_ready(pod):
                continue
            pod_name = pod["metadata"]["name"]
            output = exec_in_pod_safe(factory, pod_name, ["env"])
            if f"{env_key}={env_value}" in output:
                return True
        return False

    poll_until(
        fn=_check,
        predicate=lambda ok: ok,
        timeout=timeout,
        interval=1,
        message=f"No pod has {env_key}={env_value} in env after {timeout}s",
    )


@pytest.fixture(scope="module")
def real_factory() -> KuberokuFactory:
    return make_real_factory("e2e-cfg")


@pytest.fixture(scope="module", autouse=True)
def cleanup_namespace(real_factory: KuberokuFactory) -> Any:
    yield
    cleanup_namespace_fixture(real_factory)


def test_00_skip_if_no_cluster(real_factory: KuberokuFactory) -> None:
    skip_if_no_cluster()


def test_01_create_app(real_factory: KuberokuFactory) -> None:
    app = real_factory.apps.create("cfgtest", on_step=lambda s: None)
    assert app.name == "cfgtest"
    print(f"\n  App created in ns: {real_factory.namespace}")


def test_02_deploy_nginx(real_factory: KuberokuFactory) -> None:
    release = real_factory.deploy.deploy(
        "cfgtest",
        image="nginx:1.27-alpine",
        ports=[{"containerPort": 80, "protocol": "TCP"}],
        wait=False,
    )
    assert release.version >= 1
    wait_for_pods_ready(real_factory, "cfgtest")
    print(f"\n  Deployed v{release.version}")


def test_03_config_set(real_factory: KuberokuFactory) -> None:
    real_factory.config.set("cfgtest", {"GREETING": "hello-e2e"})
    val, source = real_factory.config.get("cfgtest", "GREETING")
    assert val == "hello-e2e"
    assert source == "configmap"
    print(f"\n  config:set GREETING=hello-e2e (source: {source})")


def test_04_verify_env_in_pod(real_factory: KuberokuFactory) -> None:
    """Wait for new pod with GREETING env var after rolling restart."""
    _wait_for_env_in_pod(real_factory, "cfgtest", "GREETING", "hello-e2e")
    print("\n  Verified GREETING=hello-e2e in pod env")


def test_05_config_get(real_factory: KuberokuFactory) -> None:
    val, source = real_factory.config.get("cfgtest", "GREETING")
    assert val == "hello-e2e"
    assert source == "configmap"
    print(f"\n  config:get GREETING={val} (source: {source})")


# ── Secret storage verification ──────────────────────────────────────


def test_06_config_set_secret(real_factory: KuberokuFactory) -> None:
    """config:set --secret should store in K8s Secret, not ConfigMap."""
    real_factory.config.set(
        "cfgtest",
        {"API_KEY": "super-secret-key-123"},
        secret=True,
    )
    val, source = real_factory.config.get("cfgtest", "API_KEY")
    assert val == "super-secret-key-123"
    assert source == "secret"
    print(f"\n  config:set --secret API_KEY (source: {source})")


def test_07_secret_stored_in_k8s_secret(real_factory: KuberokuFactory) -> None:
    """Verify API_KEY is in K8s Secret, NOT in ConfigMap."""
    # Check ConfigMap does NOT contain the secret value
    cm_name = safe_name(real_factory.prefix, "env", "cfgtest")
    cm = real_factory.k8s.get_configmap(real_factory.namespace, cm_name)
    cm_data = cm.get("data") or {}
    assert "API_KEY" not in cm_data, (
        f"API_KEY found in ConfigMap! Should be in Secret only. CM keys: {list(cm_data.keys())}"
    )
    print(f"\n  ConfigMap does NOT contain API_KEY (correct)")

    # Check Secret DOES contain it
    secret_name = safe_name(real_factory.prefix, "secret", "cfgtest")
    secret = real_factory.k8s.get_secret(real_factory.namespace, secret_name)
    secret_data = secret.get("data") or {}
    assert "API_KEY" in secret_data, (
        f"API_KEY not found in Secret! Keys: {list(secret_data.keys())}"
    )
    print(f"  Secret contains API_KEY (correct)")


def test_08_secret_visible_in_pod_env(real_factory: KuberokuFactory) -> None:
    """Secret vars should appear as env vars in the pod."""
    _wait_for_env_in_pod(
        real_factory,
        "cfgtest",
        "API_KEY",
        "super-secret-key-123",
    )
    print("\n  Verified API_KEY=super-secret-key-123 in pod env")


def test_09_list_with_sources(real_factory: KuberokuFactory) -> None:
    """list_with_sources should distinguish configmap vs secret."""
    sources = real_factory.config.list_with_sources("cfgtest")
    assert "GREETING" in sources
    assert "API_KEY" in sources
    greeting_val, greeting_src = sources["GREETING"]
    api_val, api_src = sources["API_KEY"]
    assert greeting_src == "configmap"
    assert api_src == "secret"
    print(f"\n  GREETING: source={greeting_src}, API_KEY: source={api_src}")


# ── Unset ────────────────────────────────────────────────────────────


def test_10_config_unset(real_factory: KuberokuFactory) -> None:
    real_factory.config.unset("cfgtest", ["GREETING"])
    config_vars = real_factory.config.list("cfgtest")
    assert "GREETING" not in config_vars
    print("\n  config:unset GREETING")

    # Wait for restart
    wait_for_pods_ready(real_factory, "cfgtest", timeout=120)


def test_11_verify_greeting_gone(real_factory: KuberokuFactory) -> None:
    """Exec env in pod and verify GREETING is NOT present after unset."""

    def _check_env() -> str:
        pod_name = find_running_pod(real_factory, "cfgtest")
        if not pod_name:
            return "NO_POD"
        return exec_in_pod_safe(real_factory, pod_name, ["env"])

    poll_until(
        fn=_check_env,
        predicate=lambda output: output != "NO_POD" and "GREETING=" not in output,
        timeout=120,
        interval=2,
        message="GREETING still in pod env after timeout",
    )
    print("\n  Verified GREETING removed from pod env")


def test_12_unset_secret(real_factory: KuberokuFactory) -> None:
    """Unset a secret var — unset auto-detects source."""
    real_factory.config.unset("cfgtest", ["API_KEY"])
    # Verify gone from config
    config_vars = real_factory.config.list("cfgtest")
    assert "API_KEY" not in config_vars
    print("\n  config:unset API_KEY (auto-detected from secret)")

    # Wait for restart and verify gone from pod
    def _check_env() -> str:
        pod_name = find_running_pod(real_factory, "cfgtest")
        if not pod_name:
            return "NO_POD"
        return exec_in_pod_safe(real_factory, pod_name, ["env"])

    poll_until(
        fn=_check_env,
        predicate=lambda output: output != "NO_POD" and "API_KEY=" not in output,
        timeout=120,
        interval=2,
        message="API_KEY still in pod env after timeout",
    )
    print("  Verified API_KEY removed from pod env")


# ── Cleanup ──────────────────────────────────────────────────────────


def test_13_cleanup(real_factory: KuberokuFactory) -> None:
    real_factory.apps.destroy("cfgtest")
    apps = real_factory.apps.list()
    assert not any(a.name == "cfgtest" for a in apps)
    print("\n  Cleanup complete")
