"""Tests for RegistryService — auto-detection logic."""

from __future__ import annotations

import os
from unittest.mock import PropertyMock, patch

import pytest

from kuberoku.factory import KuberokuFactory
from kuberoku.sdk.registry import RegistryService
from tests.backends.fake import FakeK8sClient


@pytest.fixture()
def registry_svc(tmp_namespace: str) -> RegistryService:
    factory = KuberokuFactory(k8s_client=FakeK8sClient(), namespace=tmp_namespace)
    return factory.registry


class TestEndpointDetection:
    def test_eks_detection(self, registry_svc: RegistryService) -> None:
        result = registry_svc._detect_from_endpoint(
            "https://ABCD1234.gr7.us-east-1.eks.amazonaws.com"
        )
        assert result is not None
        assert "ecr" in result.lower()
        assert "us-east-1" in result

    def test_gke_detection(self, registry_svc: RegistryService) -> None:
        result = registry_svc._detect_from_endpoint("https://35.123.45.67.gke.goog")
        assert result is not None
        assert "pkg.dev" in result

    def test_aks_detection(self, registry_svc: RegistryService) -> None:
        result = registry_svc._detect_from_endpoint("https://myaks-cluster.azmk8s.io")
        assert result is not None
        assert "azurecr.io" in result

    def test_unknown_endpoint(self, registry_svc: RegistryService) -> None:
        result = registry_svc._detect_from_endpoint("https://custom.k8s.example.com")
        assert result is None


class TestLocalCluster:
    def test_localhost_is_local(self, registry_svc: RegistryService) -> None:
        with patch.object(
            registry_svc, "_get_cluster_endpoint", return_value="https://localhost:6443"
        ):
            assert registry_svc.is_local_cluster() is True

    def test_127_is_local(self, registry_svc: RegistryService) -> None:
        registry_svc._cached_local = None
        with patch.object(
            registry_svc, "_get_cluster_endpoint", return_value="https://127.0.0.1:6443"
        ):
            assert registry_svc.is_local_cluster() is True

    def test_remote_is_not_local(self, registry_svc: RegistryService) -> None:
        registry_svc._cached_local = None
        with patch.object(
            registry_svc, "_get_cluster_endpoint", return_value="https://k8s.example.com:6443"
        ):
            assert registry_svc.is_local_cluster() is False

    def test_empty_endpoint_defaults_to_local(self, registry_svc: RegistryService) -> None:
        """When cluster endpoint is unreachable, assume local (safe default)."""
        registry_svc._cached_local = None
        # FakeK8sClient.cluster_endpoint returns "" by default
        assert registry_svc.is_local_cluster() is True
        assert registry_svc._cached_local is True


class TestEnvVarOverride:
    def test_env_var_override(self, registry_svc: RegistryService) -> None:
        with patch.dict(os.environ, {"KUBEROKU_REGISTRY": "my-registry.example.com"}):
            result = registry_svc.detect()
            assert result == "my-registry.example.com"


class TestDetectRemoteCluster:
    """detect() for remote clusters — not local, endpoint available."""

    def test_detect_eks_registry(self, registry_svc: RegistryService) -> None:
        """Remote EKS cluster → auto-detects ECR registry."""
        registry_svc._cached_local = None
        with (
            patch.dict(os.environ, {}, clear=False),
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://ABC.gr7.us-west-2.eks.amazonaws.com",
            ),
        ):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.detect()
        assert result is not None
        assert "ecr" in result.lower()
        assert "us-west-2" in result

    def test_detect_returns_none_for_unknown_remote(self, registry_svc: RegistryService) -> None:
        """Remote cluster with unknown provider → returns None."""
        registry_svc._cached_local = None
        with (
            patch.dict(os.environ, {}, clear=False),
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://k8s.onprem.example.com:6443",
            ),
        ):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.detect()
        assert result is None

    def test_detect_empty_endpoint_returns_none(self, registry_svc: RegistryService) -> None:
        """When endpoint is empty (local default), detect returns None."""
        with patch.dict(os.environ, {}, clear=False):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.detect()
        assert result is None

    def test_detect_remote_but_endpoint_disappears(self, registry_svc: RegistryService) -> None:
        """Remote cluster cached, but endpoint becomes unreachable → None.

        Scenario: user's kubeconfig context was valid (not local), but the
        cluster API server is now down. detect() should return None, not crash.
        """
        registry_svc._cached_local = False  # Previously determined to be remote
        # FakeK8sClient.cluster_endpoint returns "" (simulates unreachable API)
        with patch.dict(os.environ, {}, clear=False):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.detect()
        assert result is None


class TestGetOrDetect:
    def test_caches_result(self, registry_svc: RegistryService) -> None:
        registry_svc._cached_registry = "cached.example.com"
        result = registry_svc.get_or_detect()
        assert result == "cached.example.com"

    def test_returns_empty_for_local(self, registry_svc: RegistryService) -> None:
        registry_svc._cached_local = True
        # Isolate from CI env (KUBEROKU_REGISTRY may be set globally)
        with patch.dict(os.environ):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.get_or_detect()
        assert result == ""

    def test_caches_detected_registry(self, registry_svc: RegistryService) -> None:
        """When detect() finds a registry, get_or_detect caches it."""
        registry_svc._cached_local = None
        with (
            patch.dict(os.environ, {}, clear=False),
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://ABC.gr7.eu-west-1.eks.amazonaws.com",
            ),
        ):
            os.environ.pop("KUBEROKU_REGISTRY", None)
            result = registry_svc.get_or_detect()
        assert "ecr" in result.lower()
        assert registry_svc._cached_registry == result


class TestLocalLoadCommand:
    """local_load_command returns the right tool for each local cluster type."""

    def test_k3d_endpoint(self, registry_svc: RegistryService) -> None:
        with patch.object(
            registry_svc,
            "_get_cluster_endpoint",
            return_value="https://k3d-mycluster-server-0:6443",
        ):
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == ["k3d", "image", "import", "myapp:latest"]

    def test_k3d_env_var(self, registry_svc: RegistryService) -> None:
        """K3D_CLUSTER env var triggers k3d load even with generic endpoint."""
        with (
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://localhost:6443",
            ),
            patch.dict(os.environ, {"K3D_CLUSTER": "test"}),
        ):
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == ["k3d", "image", "import", "myapp:latest"]

    def test_kind_endpoint(self, registry_svc: RegistryService) -> None:
        with patch.object(
            registry_svc,
            "_get_cluster_endpoint",
            return_value="https://kind-control-plane:6443",
        ):
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == ["kind", "load", "docker-image", "myapp:latest"]

    def test_kind_env_var(self, registry_svc: RegistryService) -> None:
        """KIND_CLUSTER_NAME env var triggers kind load."""
        with (
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://localhost:6443",
            ),
            patch.dict(os.environ, {"KIND_CLUSTER_NAME": "test"}),
        ):
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == ["kind", "load", "docker-image", "myapp:latest"]

    def test_minikube_endpoint(self, registry_svc: RegistryService) -> None:
        with patch.object(
            registry_svc,
            "_get_cluster_endpoint",
            return_value="https://minikube:8443",
        ):
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == ["minikube", "image", "load", "myapp:latest"]

    def test_unknown_local_returns_empty(self, registry_svc: RegistryService) -> None:
        """Generic localhost endpoint with no tool env vars → no load command.

        Other local clusters (Colima, Docker Desktop, Rancher Desktop, etc.)
        should use the KUBEROKU_LOCAL_LOAD env var instead.
        """
        with (
            patch.object(
                registry_svc,
                "_get_cluster_endpoint",
                return_value="https://localhost:6443",
            ),
            patch.dict(os.environ, {}, clear=False),
        ):
            os.environ.pop("K3D_CLUSTER", None)
            os.environ.pop("KIND_CLUSTER_NAME", None)
            cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == []

    def test_empty_endpoint_returns_empty(self, registry_svc: RegistryService) -> None:
        """No endpoint available → no load command."""
        # FakeK8sClient.cluster_endpoint returns "" by default
        cmd = registry_svc.local_load_command("myapp:latest")
        assert cmd == []


class TestGetClusterEndpoint:
    """_get_cluster_endpoint handles errors from the K8s client."""

    def test_returns_empty_on_error(self, registry_svc: RegistryService) -> None:
        """When K8s client raises (no kubeconfig), return empty string."""
        with patch.object(
            type(registry_svc._factory.k8s),
            "cluster_endpoint",
            new_callable=PropertyMock,
            side_effect=RuntimeError("no kubeconfig found"),
        ):
            result = registry_svc._get_cluster_endpoint()
        assert result == ""
