"""E2E: multi-port add/remove verification.

Deploys an app, adds a second port, verifies both Service and Pod
have both ports, then removes the second port.

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

from __future__ import annotations

from typing import Any

import pytest

from kuberoku.factory import KuberokuFactory
from kuberoku.k8s.resources import safe_name
from tests.e2e.helpers import (
    cleanup_namespace_fixture,
    make_real_factory,
    poll_until,
    skip_if_no_cluster,
    wait_for_pods_ready,
)


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


@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_and_deploy(real_factory: KuberokuFactory) -> None:
    real_factory.apps.create("porttest", on_step=lambda s: None)
    real_factory.deploy.deploy(
        "porttest",
        image="nginx:1.27-alpine",
        ports=[{"containerPort": 80, "protocol": "TCP"}],
        wait=False,
    )
    wait_for_pods_ready(real_factory, "porttest")
    print(f"\n  Deployed in ns: {real_factory.namespace}")


def test_02_add_port_9090(real_factory: KuberokuFactory) -> None:
    real_factory.services.add_port(
        "porttest",
        "9090/tcp",
        on_step=lambda s: print(f"  {s}"),
    )
    print("\n  Added port 9090/tcp")

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


def test_03_verify_service_has_both_ports(real_factory: KuberokuFactory) -> None:
    """K8s Service.spec.ports should have both 80 and 9090."""
    svc_name = safe_name(real_factory.prefix, "porttest", "web")
    svc = real_factory.k8s.get_service(real_factory.namespace, svc_name)
    svc_ports = svc.get("spec", {}).get("ports", [])
    port_numbers = {p.get("port") for p in svc_ports}
    assert 80 in port_numbers, f"Port 80 not found: {svc_ports}"
    assert 9090 in port_numbers, f"Port 9090 not found: {svc_ports}"
    print(f"\n  Service ports: {port_numbers}")


def test_04_verify_pod_has_both_ports(real_factory: KuberokuFactory) -> None:
    """Pod containerPorts should include both 80 and 9090."""

    def _get_container_ports() -> set[int]:
        pods = wait_for_pods_ready(real_factory, "porttest")
        container = pods[0]["spec"]["containers"][0]
        return {p.get("containerPort") for p in container.get("ports", [])}

    port_numbers = poll_until(
        fn=_get_container_ports,
        predicate=lambda ports: 80 in ports and 9090 in ports,
        timeout=120,
        interval=2,
        message="Pod never got both ports 80 and 9090",
    )
    print(f"\n  Pod container ports: {port_numbers}")


def test_05_remove_port_9090(real_factory: KuberokuFactory) -> None:
    real_factory.services.remove_port(
        "porttest",
        "9090/tcp",
        on_step=lambda s: print(f"  {s}"),
    )
    print("\n  Removed port 9090/tcp")

    wait_for_pods_ready(real_factory, "porttest", timeout=120)


def test_06_verify_service_single_port(real_factory: KuberokuFactory) -> None:
    """Service should be back to only port 80."""
    svc_name = safe_name(real_factory.prefix, "porttest", "web")
    svc = real_factory.k8s.get_service(real_factory.namespace, svc_name)
    svc_ports = svc.get("spec", {}).get("ports", [])
    port_numbers = {p.get("port") for p in svc_ports}
    assert port_numbers == {80}, f"Expected only port 80: {svc_ports}"
    print(f"\n  Service ports after remove: {port_numbers}")


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