"""Tests for auto-domain on first deploy."""

from __future__ import annotations

import os
from typing import Any
from unittest import mock

import pytest

from kuberoku.factory import KuberokuFactory
from tests.backends.fake import FakeK8sClient


@pytest.fixture()
def k8s_with_infra(request: pytest.FixtureRequest) -> Any:
    """K8s client with nginx IngressClass and cert-manager.

    Fake: FakeK8sClient with infra pre-provisioned.
    Real: RealK8sClient (infra already in cluster).
    """
    backend = request.config.getoption("--backend", default="fake")
    if backend == "real":
        from tests.backends.real import RealK8sClient

        return RealK8sClient()

    client = FakeK8sClient()
    client.create_ingress_class(
        {
            "apiVersion": "networking.k8s.io/v1",
            "kind": "IngressClass",
            "metadata": {"name": "nginx"},
            "spec": {"controller": "k8s.io/ingress-nginx"},
        }
    )
    client.create_deployment(
        "cert-manager",
        {
            "apiVersion": "apps/v1",
            "kind": "Deployment",
            "metadata": {"name": "cert-manager"},
            "spec": {
                "replicas": 1,
                "selector": {"matchLabels": {"app": "cert-manager"}},
                "template": {
                    "metadata": {"labels": {"app": "cert-manager"}},
                    "spec": {"containers": [{"name": "cm", "image": "cert-manager:v1.14"}]},
                },
            },
        },
    )
    return client


@pytest.fixture()
def auto_factory(
    request: pytest.FixtureRequest, k8s_with_infra: Any, tmp_namespace: str
) -> KuberokuFactory:
    """Factory for auto-domain tests, wired to correct backend."""
    backend = request.config.getoption("--backend", default="fake")
    if backend == "real":
        from kuberoku.k8s.client import K8sClient

        return KuberokuFactory(
            k8s_client=K8sClient.from_context(),
            namespace=tmp_namespace,
            resource_prefix="k8ut",
            label_domain="test.kuberoku.com",
        )
    return KuberokuFactory(k8s_client=k8s_with_infra, namespace=tmp_namespace)


def _deploy_web(factory: KuberokuFactory, app: str) -> None:
    """Deploy a web process for the app."""
    factory.deploy.deploy(app, image=f"{app}:v1", wait=False)


class TestAutoDomain:
    def test_auto_domain_with_base_domain(self, auto_factory: KuberokuFactory) -> None:
        app = "ad-base"
        auto_factory.apps.create(app)
        _deploy_web(auto_factory, app)

        with mock.patch.dict(os.environ, {"KUBEROKU_BASE_DOMAIN": "apps.example.com"}):
            domain = auto_factory.domains.auto_domain(app)
            assert domain is not None
            assert domain.hostname == f"{app}.apps.example.com"
            assert domain.auto_generated is True

    def test_auto_domain_no_base_domain(self, auto_factory: KuberokuFactory) -> None:
        app = "ad-nobase"
        auto_factory.apps.create(app)
        domain = auto_factory.domains.auto_domain(app)
        assert domain is None

    @pytest.mark.fake_only
    def test_auto_domain_no_ingress_controller(self) -> None:
        k8s = FakeK8sClient()  # No ingress class
        with mock.patch.dict(os.environ, {"KUBEROKU_BASE_DOMAIN": "apps.example.com"}):
            factory = KuberokuFactory(k8s_client=k8s, namespace="test-ns")
            factory.apps.create("myapp")
            domain = factory.domains.auto_domain("myapp")
            assert domain is None  # Graceful: returns None

    def test_auto_domain_idempotent(self, auto_factory: KuberokuFactory) -> None:
        app = "ad-idempotent"
        auto_factory.apps.create(app)
        _deploy_web(auto_factory, app)

        with mock.patch.dict(os.environ, {"KUBEROKU_BASE_DOMAIN": "apps.example.com"}):
            d1 = auto_factory.domains.auto_domain(app)
            d2 = auto_factory.domains.auto_domain(app)
            assert d1 is not None
            assert d2 is not None
            assert d1.hostname == d2.hostname

    @pytest.mark.fake_only
    def test_auto_domain_no_cert_manager_uses_no_tls(self) -> None:
        # Fresh k8s with only ingress class (no cert-manager)
        k8s = FakeK8sClient()
        k8s.create_ingress_class(
            {
                "apiVersion": "networking.k8s.io/v1",
                "kind": "IngressClass",
                "metadata": {"name": "nginx"},
                "spec": {"controller": "k8s.io/ingress-nginx"},
            }
        )
        with mock.patch.dict(os.environ, {"KUBEROKU_BASE_DOMAIN": "apps.example.com"}):
            factory = KuberokuFactory(k8s_client=k8s, namespace="test-ns")
            factory.apps.create("myapp")
            factory.deploy.deploy("myapp", image="myapp:v1", wait=False)

            domain = factory.domains.auto_domain("myapp")
            assert domain is not None
            assert domain.tls is False

    def test_auto_domain_on_step_callback(self, auto_factory: KuberokuFactory) -> None:
        app = "ad-callback"
        # Deploy WITHOUT base_domain so auto_domain is not triggered during deploy.
        auto_factory.apps.create(app)
        _deploy_web(auto_factory, app)

        # Now set base_domain and call auto_domain explicitly.
        with mock.patch.dict(os.environ, {"KUBEROKU_BASE_DOMAIN": "apps.example.com"}):
            steps: list[str] = []
            auto_factory.domains.auto_domain(app, on_step=steps.append)
            assert len(steps) > 0
