"""Tests for deploy --wait/--no-wait rollout checking."""

from __future__ import annotations

import threading
import time
from typing import Any

import pytest

from kuberoku.exceptions import RolloutTimeoutError
from kuberoku.factory import KuberokuFactory
from kuberoku.k8s import labels
from tests.backends.fake import FakeK8sClient


def _create_running_pod(
    factory: KuberokuFactory,
    app: str,
    process: str,
    name: str,
) -> dict[str, Any]:
    k8s = factory.k8s
    assert isinstance(k8s, FakeK8sClient)
    pod: dict[str, Any] = {
        "apiVersion": "v1",
        "kind": "Pod",
        "metadata": {
            "name": name,
            "labels": labels.for_process(factory.domain, factory.prefix, app, process),
        },
        "spec": {
            "containers": [{"name": process, "image": "myapp:v1"}],
            "nodeName": "test-node",
        },
        "status": {
            "phase": "Running",
            "startTime": "2024-01-01T00:00:00Z",
            "containerStatuses": [{"ready": True}],
        },
    }
    return k8s.create_pod(factory.namespace, pod)


def _create_failed_pod(
    factory: KuberokuFactory,
    app: str,
    process: str,
    name: str,
    reason: str = "CrashLoopBackOff",
) -> dict[str, Any]:
    k8s = factory.k8s
    assert isinstance(k8s, FakeK8sClient)
    pod: dict[str, Any] = {
        "apiVersion": "v1",
        "kind": "Pod",
        "metadata": {
            "name": name,
            "labels": labels.for_process(factory.domain, factory.prefix, app, process),
        },
        "spec": {
            "containers": [{"name": process, "image": "myapp:v1"}],
            "nodeName": "test-node",
        },
        "status": {
            "phase": "Failed",
            "containerStatuses": [{"state": {"waiting": {"reason": reason}}, "ready": False}],
        },
    }
    return k8s.create_pod(factory.namespace, pod)


def test_wait_success(app_factory: KuberokuFactory) -> None:
    """Deploy with --wait succeeds when pods are ready."""

    # Create pod before deploy finishes its wait loop
    def create_pod_async() -> None:
        time.sleep(0.05)
        _create_running_pod(app_factory, "myapp", "web", "web-pod-1")

    t = threading.Thread(target=create_pod_async)
    t.start()
    release = app_factory.deploy.deploy("myapp", image="myapp:v1", wait=True, timeout=5)
    t.join(timeout=10)
    assert release.version == 1


def test_wait_timeout(app_factory: KuberokuFactory) -> None:
    """Deploy with --wait times out when pods don't come up."""
    with pytest.raises(RolloutTimeoutError, match="timed out"):
        app_factory.deploy.deploy("myapp", image="myapp:v1", wait=True, timeout=0.3)


def test_wait_crash_detection(app_factory: KuberokuFactory) -> None:
    """Deploy with --wait detects crashed pods."""

    def create_failed_pod_async() -> None:
        time.sleep(0.05)
        _create_failed_pod(app_factory, "myapp", "web", "web-crash-1")

    t = threading.Thread(target=create_failed_pod_async)
    t.start()
    with pytest.raises(RolloutTimeoutError, match="CrashLoopBackOff"):
        app_factory.deploy.deploy("myapp", image="myapp:v1", wait=True, timeout=5)
    t.join(timeout=10)


def test_no_wait_skips(app_factory: KuberokuFactory) -> None:
    """Deploy with --no-wait skips rollout polling."""
    release = app_factory.deploy.deploy("myapp", image="myapp:v1", wait=False)
    assert release.version == 1


def test_wait_on_step_progress(app_factory: KuberokuFactory) -> None:
    """on_step reports progress during wait."""
    steps: list[str] = []

    def create_pod_async() -> None:
        time.sleep(0.05)
        _create_running_pod(app_factory, "myapp", "web", "web-pod-1")

    t = threading.Thread(target=create_pod_async)
    t.start()
    app_factory.deploy.deploy(
        "myapp", image="myapp:v1", wait=True, timeout=5, on_step=steps.append
    )
    t.join(timeout=10)
    assert any("Waiting" in s or "Rollout" in s or "complete" in s.lower() for s in steps)
