"""CLI commands for creating resources (verb-first: k4s create <resource>)."""
from pathlib import Path

import click

from k4s.cli.state import CliState
from k4s.cli.verbosity import apply_command_ui_overrides
from k4s.core.cluster import ClusterError, Rke2ClusterCreator, build_nodes_from_contexts, run_preflight
from k4s.core.executor import Executor
from k4s.recipes.common.linux import install_kubectl
from k4s.ui.ui import Ui


class OrderedCreateGroup(click.Group):
    _order = ["cluster"]

    def list_commands(self, ctx):
        return self._order


@click.group(cls=OrderedCreateGroup)
def create():
    """Create Kubernetes clusters and other resources."""
    pass


@create.command("cluster")
@click.option("--type", "cluster_type", type=click.Choice(["rke2"], case_sensitive=False), default="rke2", show_default=True)
@click.option("--control-plane", "control_planes", multiple=True, required=True, help="Context name for a control-plane node.")
@click.option("--worker", "workers", multiple=True, help="Context name for a worker node.")
@click.option("--cni", type=click.Choice(["canal", "cilium", "calico"], case_sensitive=False), default="canal", show_default=True)
@click.option(
    "--tls-san",
    "extra_tls_san",
    multiple=True,
    help=(
        "Extra IP or hostname to add to the RKE2 TLS SAN list (repeatable). "
        "The control-plane's host is always included automatically."
    ),
)
@click.option(
    "--export-kubeconfig",
    "export_context",
    default=None,
    help="VM context name to copy the kubeconfig to after cluster creation (e.g. the Dataiku machine).",
)
@click.option(
    "--export-path",
    default=None,
    help="Path on the target VM where the kubeconfig will be written (defaults to ~/.kube/config of the target user).",
)
@click.option(
    "--with-kubectl/--no-kubectl",
    "install_kubectl_flag",
    default=True,
    show_default=True,
    help="Install kubectl on all control-plane nodes after cluster creation.",
)
@click.option("--dry-run", is_flag=True, help="Print the plan without applying changes.")
@click.option("-q", "--quiet", is_flag=True, help="Only print final results and errors.")
@click.option("-v", "--verbose", count=True, help="Increase verbosity (-v logs, -vv debug).")
@click.pass_context
def cluster(
    ctx,
    cluster_type,
    control_planes,
    workers,
    cni,
    extra_tls_san,
    export_context,
    export_path,
    install_kubectl_flag,
    dry_run,
    quiet,
    verbose,
):
    """Create a Kubernetes cluster on the specified context nodes.

    Runs preflight checks, installs RKE2 on control-plane and worker nodes,
    and fetches the kubeconfig to ~/.kube/k4s/default.yaml.

    \b
    Examples:
      k4s create cluster --control-plane cp1 --worker w1 --worker w2
      k4s create cluster --control-plane cp1 --cni cilium --no-kubectl
      k4s create cluster --control-plane cp1 --tls-san my.domain.com --export-kubeconfig dataiku
    """
    state: CliState = ctx.obj["state"]
    apply_command_ui_overrides(ctx, state, quiet=quiet, verbose=verbose)
    ui = state.ui

    if cluster_type.lower() != "rke2":
        ui.error(f"Unsupported cluster type: {cluster_type}")
        ctx.exit(2)

    nodes = None
    try:
        nodes = build_nodes_from_contexts(
            state.contexts, control_planes=list(control_planes), workers=list(workers)
        )
        run_preflight(ui, nodes=nodes)
        Rke2ClusterCreator(ui=ui).create(
            name="default",
            nodes=nodes,
            cni=cni,
            tls_san=list(extra_tls_san) if extra_tls_san else None,
            dry_run=dry_run,
        )
    except (KeyError, ClusterError) as e:
        ui.error(str(e))
        ctx.exit(1)

    if dry_run:
        ui.info("Dry-run: cluster installation was not executed.")
        return

    primary_cp = next(n for n in nodes if n.role == "control-plane")

    if install_kubectl_flag:
        cp_nodes = [n for n in nodes if n.role == "control-plane"]
        for n in cp_nodes:
            with ui.step(f"Install kubectl on {n.context.name}"):
                ui.log("Installing kubectl via official Kubernetes apt repository.")
                with Executor(n.context.to_server_config()) as ex:
                    install_kubectl(ex)

    state.history.append(
        action="cluster-create",
        product="cluster",
        context=primary_cp.context.name,
        host=primary_cp.context.host,
        params={
            "type": "rke2",
            "cni": cni,
            "control_planes": list(control_planes),
            "workers": list(workers),
        },
    )
    ui.success("Cluster created successfully.")

    kubeconfig_path = str(Path.home() / ".kube" / "k4s" / "default.yaml")
    ui.info("")
    ui.info("Hint: Create a k8s context for Helm-based installs:")
    ui.info(f"  k4s context add <name> --type k8s --kubeconfig {kubeconfig_path} --current")

    if export_context:
        _export_kubeconfig_to_vm(ui, state, primary_cp, export_context, export_path)


def _export_kubeconfig_to_vm(
    ui: Ui,
    state,
    primary_cp,
    export_context_name: str,
    export_path: str | None,
) -> None:
    """Copy the cluster kubeconfig to a VM context."""
    try:
        target = state.contexts.resolve(export_context_name)
    except KeyError:
        ui.warning(
            f"Context '{export_context_name}' not found; skipping kubeconfig export.\n"
            f"  Add it with: k4s context add {export_context_name} ..."
        )
        return

    target_user = target.username or "root"
    target_sudo = target_user.lower() not in {"root"}
    resolved_path = export_path or (
        f"/home/{target_user}/.kube/config" if target_sudo else "/root/.kube/config"
    )

    with ui.step(f"Export kubeconfig to {export_context_name} ({target.host})"):
        cp_sudo = (primary_cp.context.username or "").lower() not in {"root"}
        with Executor(primary_cp.context.to_server_config()) as cp_ex:
            content = cp_ex.read_remote_file_content("/etc/rancher/rke2/rke2.yaml", use_sudo=cp_sudo)

        content = content.replace("127.0.0.1", primary_cp.context.host)

        _sudo = "sudo -n " if target_sudo else ""
        with Executor(target.to_server_config()) as t_ex:
            kube_dir = str(Path(resolved_path).parent)
            t_ex.execute(f"{_sudo}mkdir -p {kube_dir}", use_sudo=False)
            t_ex.execute(f"{_sudo}chmod 700 {kube_dir}", use_sudo=False)
            if target_sudo:
                t_ex.execute(f"sudo -n chown {target_user}:{target_user} {kube_dir}", use_sudo=False)

            rc, out, err = t_ex.execute(
                f"{_sudo}tee {resolved_path}",
                use_sudo=False,
                stdin_data=content,
            )
            if rc != 0:
                raise RuntimeError(f"Failed to write kubeconfig to {resolved_path}: {err or out}")

            t_ex.execute(f"{_sudo}chmod 600 {resolved_path}", use_sudo=False)
            if target_sudo:
                t_ex.execute(f"sudo -n chown {target_user}:{target_user} {resolved_path}", use_sudo=False)

            # Only persist KUBECONFIG in shell profiles for non-default paths.
            default_path = f"/home/{target_user}/.kube/config" if target_sudo else "/root/.kube/config"
            if resolved_path != default_path:
                export_line = f"export KUBECONFIG={resolved_path}"
                for rc_file in ("~/.bashrc", "~/.profile"):
                    t_ex.execute(
                        f"grep -qF {export_line!r} {rc_file} 2>/dev/null "
                        f"|| echo {export_line!r} >> {rc_file}",
                        use_sudo=False,
                    )

    ui.info(f"Kubeconfig written to {target.host}:{resolved_path}")
