"""``citadel uninstall`` — remove Citadel from a Kubernetes cluster."""

from __future__ import annotations

import sys

from kubernetes import client, config
from kubernetes.client.rest import ApiException


def _load_kube_config() -> None:
    try:
        config.load_incluster_config()
    except config.ConfigException:
        config.load_kube_config()


def _delete_ignore_404(fn: object, *args: object, **kwargs: object) -> bool:
    """Call *fn* and swallow 404 (already deleted).  Returns True if deleted."""
    try:
        fn(*args, **kwargs)  # type: ignore[operator]
        return True
    except ApiException as exc:
        if exc.status == 404:
            return False
        raise


def _log(msg: str) -> None:
    print(f"  {msg}", file=sys.stderr)


def uninstall(namespace: str = "citadel-system", delete_crds: bool = False) -> bool:
    """Remove Citadel resources from the cluster.  Returns True on success."""
    _load_kube_config()

    core = client.CoreV1Api()
    apps = client.AppsV1Api()
    rbac = client.RbacAuthorizationV1Api()
    ext = client.ApiextensionsV1Api()
    custom = client.CustomObjectsApi()

    # 1. Delete Deployment
    _log("Deleting Deployment citadel-operator...")
    _delete_ignore_404(apps.delete_namespaced_deployment, "citadel-operator", namespace)

    # 2. Delete default policies (those managed by citadel-cli)
    _log("Deleting default NodeTerminationPolicies...")
    try:
        policies = custom.list_cluster_custom_object(
            group="citadel.dev",
            version="v1alpha1",
            plural="nodeterminationpolicies",
            label_selector="app.kubernetes.io/part-of=citadel",
        )
        for item in policies.get("items", []):
            name = item["metadata"]["name"]
            _delete_ignore_404(
                custom.delete_cluster_custom_object,
                group="citadel.dev",
                version="v1alpha1",
                plural="nodeterminationpolicies",
                name=name,
            )
            _log(f"  Deleted policy {name}")
    except ApiException as exc:
        if exc.status != 404:
            _log(f"  Warning: could not list policies: {exc.reason}")

    # 3. Delete ClusterRoleBinding
    _log("Deleting ClusterRoleBinding citadel-operator...")
    _delete_ignore_404(rbac.delete_cluster_role_binding, "citadel-operator")

    # 4. Delete ClusterRole
    _log("Deleting ClusterRole citadel-operator...")
    _delete_ignore_404(rbac.delete_cluster_role, "citadel-operator")

    # 5. Delete ServiceAccount
    _log("Deleting ServiceAccount citadel-operator...")
    _delete_ignore_404(core.delete_namespaced_service_account, "citadel-operator", namespace)

    # 6. Optionally delete CRDs (destructive — removes all instances)
    if delete_crds:
        _log("Deleting CRDs (--delete-crds)...")
        for crd_name in (
            "nodeterminationpolicies.citadel.dev",
            "namespaceinfrastructures.citadel.dev",
        ):
            if _delete_ignore_404(ext.delete_custom_resource_definition, crd_name):
                _log(f"  Deleted CRD {crd_name}")
                # Wait for CRD to actually disappear; clear stuck finalizers
                import time
                for _ in range(15):
                    try:
                        crd_obj = ext.read_custom_resource_definition(crd_name)
                        if crd_obj.metadata.deletion_timestamp and crd_obj.metadata.finalizers:
                            # Use merge-patch to avoid 409 Conflict
                            ext.patch_custom_resource_definition(
                                crd_name,
                                body={"metadata": {"finalizers": None}},
                            )
                        time.sleep(1)
                    except ApiException as exc:
                        if exc.status == 404:
                            break
                        if exc.status == 409:
                            time.sleep(1)  # Retry on conflict
                            continue
                        raise
    else:
        _log("Skipping CRD deletion (use --delete-crds to remove)")

    # 7. Delete namespace if empty
    _log(f"Deleting namespace {namespace}...")
    try:
        pods = core.list_namespaced_pod(namespace)
        if len(pods.items) == 0:
            _delete_ignore_404(core.delete_namespace, namespace)
            _log(f"  Namespace {namespace} deleted")
        else:
            _log(f"  Namespace {namespace} still has {len(pods.items)} pods — keeping")
    except ApiException as exc:
        if exc.status == 404:
            _log(f"  Namespace {namespace} already gone")
        else:
            _log(f"  Warning: could not inspect namespace: {exc.reason}")

    print("\n  Citadel uninstalled.", file=sys.stderr)
    return True
