"""
Export EDA CRD resources from a Kubernetes cluster to YAML files.

Authentication: this command uses only kubectl (the 4th / cluster-access method).
No EDA URL, Keycloak, or EDA SaaS credentials are required.
Ensure kubectl is in PATH and your kubeconfig (or in-cluster config) is valid.
"""

import json
import logging
import os
import shutil
import subprocess
import tarfile
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from pathlib import Path

import yaml
from rich.console import Console
from rich.table import Table

logger = logging.getLogger(__name__)
console = Console()


def run_kubectl(args: list[str]) -> dict:
    logger.debug("Running command: kubectl %s", " ".join(args))
    result = subprocess.run(
        ["kubectl", *args],
        check=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )
    return json.loads(result.stdout)


def get_eda_crd_resources(group_suffix: str) -> list[str]:
    crds = run_kubectl(["get", "crd", "-o", "json"])
    resources: list[str] = []
    for crd in crds.get("items", []):
        spec = crd.get("spec", {})
        group = spec.get("group")
        if not group or not group.endswith(group_suffix):
            continue
        plural = spec.get("names", {}).get("plural")
        if plural:
            resources.append(f"{plural}.{group}")
    return resources


def write_resources(
    resource: str, namespace: str, out_dir: Path, set_namespace: str | None
) -> tuple[str, int, Path | None]:
    data = run_kubectl(["get", resource, "-n", namespace, "-o", "json"])
    items = data.get("items", [])
    if not items:
        return resource, 0, None

    plural, group = resource.split(".", 1)
    suffix = group.replace("eda.", "", 1)
    output = out_dir / f"{plural}.{suffix}.yaml"

    filtered_items = []
    for item in items:
        metadata = item.get("metadata") or {}
        labels = metadata.get("labels") or {}
        if labels.get("eda.nokia.com/source") == "derived":
            continue

        item.pop("status", None)
        filtered_metadata = {
            "name": metadata.get("name"),
            "namespace": set_namespace or metadata.get("namespace"),
        }
        if labels:
            filtered_metadata["labels"] = labels
        annotations = metadata.get("annotations")
        if annotations:
            filtered_metadata["annotations"] = annotations
        item["metadata"] = filtered_metadata
        filtered_items.append(item)

    if not filtered_items:
        return resource, 0, None

    with output.open("w", encoding="utf-8") as handle:
        for index, item in enumerate(filtered_items):
            if index:
                handle.write("---\n")
            yaml.safe_dump(item, handle, sort_keys=False)

    return resource, len(filtered_items), output


def run_export(
    namespace: str = "eda",
    out_dir: Path = Path("eda-resources"),
    set_namespace: str | None = None,
    group: str = "eda.nokia.com",
    archive: bool = False,
) -> None:
    """
    Export EDA CRD resources from the cluster to YAML. Uses kubectl only (no .env/EDA auth).
    Uses the module logger (configured by main's setup_logging).
    """
    if shutil.which("kubectl") is None:
        console.print(
            "[red]Required binary not found:[/red] `kubectl` is not in PATH.\n"
            "Export uses kubectl as its only authentication/access method. "
            "Install kubectl and ensure it is available (e.g. kubeconfig or in-cluster config)."
        )
        raise SystemExit(1)

    out_dir = Path(out_dir)
    output_dir = out_dir / namespace
    output_dir.mkdir(parents=True, exist_ok=True)
    logger.info("Exporting resources from namespace '%s'", namespace)

    try:
        resources = get_eda_crd_resources(group)
    except subprocess.CalledProcessError as exc:
        console.print(
            f"[red]kubectl failed while listing CRDs:[/red]\n{exc.stderr.strip()}"
        )
        raise SystemExit(1) from exc
    except json.JSONDecodeError as exc:
        console.print("[red]kubectl did not return valid JSON while listing CRDs.[/red]")
        raise SystemExit(1) from exc

    if not resources:
        console.print(f"[yellow]No CRDs found for group suffix '{group}'.[/yellow]")
        return

    logger.info("Discovered %d resource kinds", len(resources))
    max_workers = os.cpu_count() or 1

    results: list[tuple[str, int, Path | None]] = []
    try:
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            for result in executor.map(
                lambda r: write_resources(r, namespace, output_dir, set_namespace),
                resources,
            ):
                results.append(result)
    except subprocess.CalledProcessError as exc:
        console.print(
            f"[red]kubectl failed while exporting resources:[/red]\n{exc.stderr.strip()}"
        )
        raise SystemExit(1) from exc
    except json.JSONDecodeError as exc:
        console.print(
            "[red]kubectl returned invalid JSON while exporting resources.[/red]"
        )
        raise SystemExit(1) from exc

    written = [(resource, count, path) for resource, count, path in results if path]
    if written:
        table = Table(title="Exported EDA Resources")
        table.add_column("Resource", style="cyan")
        table.add_column("Items", justify="right", style="green")
        table.add_column("File", style="magenta")
        for resource, count, path in sorted(written):
            table.add_row(resource, str(count), str(path))
        console.print(table)
    else:
        console.print("[yellow]No resources written.[/yellow]")

    if archive:
        if any(output_dir.iterdir()):
            timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
            archive_name = f"{namespace}-{timestamp}.tar.gz"
            archive_path = out_dir / archive_name
            with tarfile.open(archive_path, "w:gz") as tar:
                tar.add(output_dir, arcname=namespace)
            console.print(f"[green]Created archive:[/green] {archive_path}")
        else:
            console.print(
                f"[yellow]No resources written to {output_dir}. Skipping archive.[/yellow]"
            )
