"""Tests for certificate management.

Tests the CertificateConfig dataclass validation and the CertificateManager
static YAML generation. Cluster-dependent operations (list, check_expiry,
rotate) are tested with mocked Kubernetes API clients.

No Kubernetes cluster required.
"""

import pytest
from datetime import datetime, timezone, timedelta
from unittest.mock import MagicMock, patch

from citadel_operator.k3s.certificates import (
    CERT_MANAGER_GROUP,
    CERT_MANAGER_VERSION,
    CertificateConfig,
    CertificateManager,
    DEFAULT_DURATION_HOURS,
    DEFAULT_RENEW_BEFORE_HOURS,
    MAX_DURATION_HOURS,
    MIN_DURATION_HOURS,
    MIN_RENEW_BEFORE_HOURS,
)


# ============================================================
# CertificateConfig — Construction & Validation
# ============================================================


class TestCertificateConfigConstruction:
    """Tests for CertificateConfig creation and validation."""

    def test_minimal_config(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.default.svc.cluster.local",
        )
        assert config.issuer_name == "internal-ca"
        assert config.common_name == "my-service.default.svc.cluster.local"
        assert config.dns_names == ()
        assert config.duration_hours == DEFAULT_DURATION_HOURS
        assert config.renew_before_hours == DEFAULT_RENEW_BEFORE_HOURS
        assert config.is_ca is False
        assert config.secret_name is None
        assert config.namespace == "default"

    def test_full_config(self):
        config = CertificateConfig(
            issuer_name="letsencrypt-prod",
            common_name="api.example.com",
            dns_names=("api.example.com", "www.api.example.com"),
            duration_hours=720,
            renew_before_hours=168,
            is_ca=False,
            secret_name="api-tls",
            namespace="production",
            private_key_algorithm="RSA",
            private_key_size=4096,
            usages=("server auth", "digital signature"),
        )
        assert config.issuer_name == "letsencrypt-prod"
        assert config.dns_names == ("api.example.com", "www.api.example.com")
        assert config.duration_hours == 720
        assert config.renew_before_hours == 168
        assert config.secret_name == "api-tls"
        assert config.namespace == "production"
        assert config.private_key_algorithm == "RSA"
        assert config.private_key_size == 4096

    def test_frozen(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
        )
        with pytest.raises(AttributeError):
            config.issuer_name = "changed"

    def test_ca_certificate_config(self):
        config = CertificateConfig(
            issuer_name="citadel-selfsigned-bootstrap",
            common_name="citadel-internal-ca",
            is_ca=True,
            duration_hours=87600,  # 10 years
            renew_before_hours=8760,  # 1 year
        )
        assert config.is_ca is True
        assert config.duration_hours == 87600

    def test_default_usages(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
        )
        assert config.usages == ("server auth", "digital signature", "key encipherment")

    def test_client_auth_usages(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="client.local",
            usages=("client auth", "digital signature"),
        )
        assert "client auth" in config.usages
        assert "server auth" not in config.usages


class TestCertificateConfigValidation:
    """Tests for CertificateConfig __post_init__ validation."""

    def test_empty_issuer_name_raises(self):
        with pytest.raises(ValueError, match="issuer_name must not be empty"):
            CertificateConfig(issuer_name="", common_name="test.local")

    def test_empty_common_name_raises(self):
        with pytest.raises(ValueError, match="common_name must not be empty"):
            CertificateConfig(issuer_name="internal-ca", common_name="")

    def test_dns_names_must_be_tuple(self):
        with pytest.raises(TypeError, match="dns_names must be a tuple"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                dns_names=["test.local"],  # type: ignore[arg-type]
            )

    def test_duration_too_short_raises(self):
        with pytest.raises(ValueError, match="duration_hours must be >="):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                duration_hours=0,
            )

    def test_duration_too_long_raises(self):
        with pytest.raises(ValueError, match="duration_hours must be <="):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                duration_hours=MAX_DURATION_HOURS + 1,
            )

    def test_renew_before_too_short_raises(self):
        with pytest.raises(ValueError, match="renew_before_hours must be >="):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                renew_before_hours=0,
            )

    def test_renew_before_exceeds_duration_raises(self):
        with pytest.raises(ValueError, match="renew_before_hours.*must be less than.*duration"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                duration_hours=720,
                renew_before_hours=720,
            )

    def test_renew_before_greater_than_duration_raises(self):
        with pytest.raises(ValueError, match="renew_before_hours.*must be less than.*duration"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                duration_hours=720,
                renew_before_hours=721,
            )

    def test_invalid_key_algorithm_raises(self):
        with pytest.raises(ValueError, match="private_key_algorithm must be"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                private_key_algorithm="DSA",
            )

    def test_invalid_ecdsa_key_size_raises(self):
        with pytest.raises(ValueError, match="ECDSA key size must be 256 or 384"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                private_key_algorithm="ECDSA",
                private_key_size=512,
            )

    def test_invalid_rsa_key_size_raises(self):
        with pytest.raises(ValueError, match="RSA key size must be 2048 or 4096"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                private_key_algorithm="RSA",
                private_key_size=1024,
            )

    def test_valid_ecdsa_384(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            private_key_algorithm="ECDSA",
            private_key_size=384,
        )
        assert config.private_key_size == 384

    def test_valid_rsa_2048(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            private_key_algorithm="RSA",
            private_key_size=2048,
        )
        assert config.private_key_size == 2048

    def test_extra_labels_must_be_dict(self):
        with pytest.raises(TypeError, match="extra_labels must be a dict"):
            CertificateConfig(
                issuer_name="internal-ca",
                common_name="test.local",
                extra_labels="not-a-dict",  # type: ignore[arg-type]
            )


class TestCertificateConfigProperties:
    """Tests for CertificateConfig computed properties."""

    def test_effective_secret_name_explicit(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            secret_name="my-secret",
        )
        assert config.effective_secret_name == "my-secret"

    def test_effective_secret_name_auto_generated(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.default.svc.cluster.local",
        )
        assert config.effective_secret_name == "my-service-default-svc-cluster-local-tls"

    def test_effective_secret_name_wildcard(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="*.internal.citadel.local",
        )
        assert config.effective_secret_name == "wildcard-internal-citadel-local-tls"

    def test_effective_dns_names_explicit(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            dns_names=("test.local", "test2.local"),
        )
        assert config.effective_dns_names == ("test.local", "test2.local")

    def test_effective_dns_names_fallback(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
        )
        assert config.effective_dns_names == ("test.local",)


# ============================================================
# CertificateManager.create_certificate — YAML Generation
# ============================================================


class TestCreateCertificate:
    """Tests for the static create_certificate method."""

    def test_basic_certificate_structure(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.default.svc.cluster.local",
            dns_names=("my-service.default.svc.cluster.local", "my-service"),
        )
        result = CertificateManager.create_certificate(config)

        assert result["apiVersion"] == f"{CERT_MANAGER_GROUP}/{CERT_MANAGER_VERSION}"
        assert result["kind"] == "Certificate"
        assert "metadata" in result
        assert "spec" in result

    def test_metadata(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.default.svc.cluster.local",
            namespace="production",
        )
        result = CertificateManager.create_certificate(config)

        assert result["metadata"]["namespace"] == "production"
        assert result["metadata"]["labels"]["app.kubernetes.io/managed-by"] == "citadel-operator"

    def test_resource_name_derived_from_common_name(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.default.svc.cluster.local",
        )
        result = CertificateManager.create_certificate(config)

        assert result["metadata"]["name"] == "my-service-default-svc-cluster-local"

    def test_wildcard_resource_name(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="*.internal.citadel.local",
        )
        result = CertificateManager.create_certificate(config)

        assert result["metadata"]["name"] == "wildcard-internal-citadel-local"

    def test_spec_duration_format(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            duration_hours=2160,
            renew_before_hours=720,
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["duration"] == "2160h"
        assert result["spec"]["renewBefore"] == "720h"

    def test_spec_issuer_ref(self):
        config = CertificateConfig(
            issuer_name="letsencrypt-prod",
            common_name="api.example.com",
        )
        result = CertificateManager.create_certificate(config)

        issuer_ref = result["spec"]["issuerRef"]
        assert issuer_ref["name"] == "letsencrypt-prod"
        assert issuer_ref["kind"] == "ClusterIssuer"
        assert issuer_ref["group"] == CERT_MANAGER_GROUP

    def test_spec_private_key(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            private_key_algorithm="ECDSA",
            private_key_size=256,
        )
        result = CertificateManager.create_certificate(config)

        pk = result["spec"]["privateKey"]
        assert pk["algorithm"] == "ECDSA"
        assert pk["size"] == 256
        assert pk["rotationPolicy"] == "Always"

    def test_spec_dns_names(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.local",
            dns_names=("my-service.local", "my-service"),
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["dnsNames"] == ["my-service.local", "my-service"]

    def test_spec_dns_names_fallback_to_common_name(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.local",
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["dnsNames"] == ["my-service.local"]

    def test_spec_usages(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            usages=("client auth", "digital signature"),
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["usages"] == ["client auth", "digital signature"]

    def test_spec_is_ca(self):
        config = CertificateConfig(
            issuer_name="citadel-selfsigned-bootstrap",
            common_name="citadel-internal-ca",
            is_ca=True,
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["isCA"] is True

    def test_spec_secret_name_auto(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.local",
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["secretName"] == "my-service-local-tls"

    def test_spec_secret_name_explicit(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="my-service.local",
            secret_name="custom-secret",
        )
        result = CertificateManager.create_certificate(config)

        assert result["spec"]["secretName"] == "custom-secret"

    def test_empty_usages_omitted(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            usages=(),
        )
        result = CertificateManager.create_certificate(config)

        assert "usages" not in result["spec"]

    def test_extra_labels_merged(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            extra_labels={"app.kubernetes.io/requested-by": "apollo"},
        )
        result = CertificateManager.create_certificate(config)

        labels = result["metadata"]["labels"]
        assert labels["app.kubernetes.io/managed-by"] == "citadel-operator"
        assert labels["app.kubernetes.io/requested-by"] == "apollo"

    def test_extra_labels_empty_default(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
        )
        result = CertificateManager.create_certificate(config)

        labels = result["metadata"]["labels"]
        assert labels == {"app.kubernetes.io/managed-by": "citadel-operator"}

    def test_extra_labels_multiple(self):
        config = CertificateConfig(
            issuer_name="internal-ca",
            common_name="test.local",
            extra_labels={
                "app.kubernetes.io/requested-by": "apollo",
                "team": "platform",
            },
        )
        result = CertificateManager.create_certificate(config)

        labels = result["metadata"]["labels"]
        assert labels["app.kubernetes.io/managed-by"] == "citadel-operator"
        assert labels["app.kubernetes.io/requested-by"] == "apollo"
        assert labels["team"] == "platform"


# ============================================================
# CertificateManager — Cluster Operations (Mocked)
# ============================================================


class TestListCertificates:
    """Tests for list_certificates with mocked Kubernetes API."""

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_list_returns_items(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()
        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {"metadata": {"name": "cert-1"}},
                {"metadata": {"name": "cert-2"}},
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        certs = manager.list_certificates(namespace="default")

        assert len(certs) == 2
        assert certs[0]["metadata"]["name"] == "cert-1"

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_list_empty_namespace(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()
        mock_custom.list_namespaced_custom_object.return_value = {"items": []}

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        certs = manager.list_certificates(namespace="empty-ns")

        assert certs == []

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_list_crd_not_installed(self, mock_client, mock_config):
        from kubernetes.client.exceptions import ApiException

        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()
        mock_custom.list_namespaced_custom_object.side_effect = ApiException(
            status=404, reason="Not Found"
        )

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        certs = manager.list_certificates(namespace="default")

        assert certs == []


class TestCheckExpiry:
    """Tests for check_expiry with mocked Kubernetes API."""

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_healthy_cert(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        future = datetime.now(timezone.utc) + timedelta(days=60)
        renewal = datetime.now(timezone.utc) + timedelta(days=30)

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "healthy-cert"},
                    "status": {
                        "notAfter": future.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "renewalTime": renewal.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "True"}],
                    },
                }
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 1
        name, expiry, needs_renewal = results[0]
        assert name == "healthy-cert"
        assert not needs_renewal

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_expired_cert(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        past = datetime.now(timezone.utc) - timedelta(days=1)

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "expired-cert"},
                    "status": {
                        "notAfter": past.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "True"}],
                    },
                }
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 1
        _, _, needs_renewal = results[0]
        assert needs_renewal is True

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_renewal_window(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        future = datetime.now(timezone.utc) + timedelta(days=10)
        # renewalTime is in the past — cert-manager should have triggered renewal
        renewal_past = datetime.now(timezone.utc) - timedelta(days=1)

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "renewing-cert"},
                    "status": {
                        "notAfter": future.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "renewalTime": renewal_past.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "True"}],
                    },
                }
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 1
        _, _, needs_renewal = results[0]
        assert needs_renewal is True

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_not_ready_condition(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        future = datetime.now(timezone.utc) + timedelta(days=60)

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "not-ready-cert"},
                    "status": {
                        "notAfter": future.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "False"}],
                    },
                }
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 1
        _, _, needs_renewal = results[0]
        assert needs_renewal is True

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_no_not_after(self, mock_client, mock_config):
        """Certificates without notAfter (not yet issued) are skipped."""
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "pending-cert"},
                    "status": {},
                }
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 0

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_check_expiry_multiple_certs(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        future_healthy = datetime.now(timezone.utc) + timedelta(days=60)
        future_expiring = datetime.now(timezone.utc) - timedelta(hours=1)

        mock_custom.list_namespaced_custom_object.return_value = {
            "items": [
                {
                    "metadata": {"name": "healthy"},
                    "status": {
                        "notAfter": future_healthy.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "True"}],
                    },
                },
                {
                    "metadata": {"name": "expired"},
                    "status": {
                        "notAfter": future_expiring.strftime("%Y-%m-%dT%H:%M:%SZ"),
                        "conditions": [{"type": "Ready", "status": "True"}],
                    },
                },
            ]
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        results = manager.check_expiry(namespace="default")

        assert len(results) == 2
        names = {r[0] for r in results}
        assert names == {"healthy", "expired"}
        # Find the expired one
        expired_result = [r for r in results if r[0] == "expired"][0]
        assert expired_result[2] is True


class TestRotateCertificate:
    """Tests for rotate_certificate with mocked Kubernetes API."""

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_rotate_success(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_core = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = mock_core

        mock_custom.get_namespaced_custom_object.return_value = {
            "spec": {"secretName": "my-cert-tls"}
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        result = manager.rotate_certificate("my-cert", namespace="default")

        assert result is True
        mock_core.delete_namespaced_secret.assert_called_once_with(
            name="my-cert-tls",
            namespace="default",
        )

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_rotate_cert_not_found(self, mock_client, mock_config):
        from kubernetes.client.exceptions import ApiException

        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        mock_custom.get_namespaced_custom_object.side_effect = ApiException(
            status=404, reason="Not Found"
        )

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        result = manager.rotate_certificate("nonexistent", namespace="default")

        assert result is False

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_rotate_secret_not_found(self, mock_client, mock_config):
        from kubernetes.client.exceptions import ApiException

        mock_custom = MagicMock()
        mock_core = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = mock_core

        mock_custom.get_namespaced_custom_object.return_value = {
            "spec": {"secretName": "missing-secret"}
        }
        mock_core.delete_namespaced_secret.side_effect = ApiException(
            status=404, reason="Not Found"
        )

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        result = manager.rotate_certificate("my-cert", namespace="default")

        assert result is False

    @patch("citadel_operator.k3s.certificates.config")
    @patch("citadel_operator.k3s.certificates.client")
    def test_rotate_no_secret_name_in_spec(self, mock_client, mock_config):
        mock_custom = MagicMock()
        mock_client.CustomObjectsApi.return_value = mock_custom
        mock_client.CoreV1Api.return_value = MagicMock()

        mock_custom.get_namespaced_custom_object.return_value = {
            "spec": {}
        }

        manager = CertificateManager(kubeconfig="/fake/kubeconfig")
        result = manager.rotate_certificate("my-cert", namespace="default")

        assert result is False
