import click
from kubernetes import client as k8s_client

from vantage6.common import error, info, warning
from vantage6.common.globals import (
    DEFAULT_NODE_IMAGE,
    DEFAULT_NODE_IMAGE_WO_TAG,
    InstanceType,
)

from vantage6.cli import __version__
from vantage6.cli.common.attach import attach_logs
from vantage6.cli.common.decorator import click_insert_context
from vantage6.cli.common.start import (
    helm_install,
    prestart_checks,
)
from vantage6.cli.common.utils import create_directory_if_not_exists
from vantage6.cli.context.node import NodeContext
from vantage6.cli.globals import ChartName, InfraComponentName
from vantage6.cli.k8s_config import KubernetesConfig, select_k8s_config
from vantage6.cli.node.common import create_client
from vantage6.cli.utils_kubernetes import get_core_api_with_ssl_handling


@click.command()
@click.option("--context", default=None, help="Kubernetes context to use")
@click.option("--namespace", default=None, help="Kubernetes namespace to use")
@click.option(
    "--attach/--detach",
    default=False,
    help="Show node logs on the current console after starting the node",
)
@click.option("--local-chart-dir", default=None, help="Local chart directory to use")
@click.option("--chart-version", default=None, help="Chart version to use")
@click.option("--sandbox/--no-sandbox", "sandbox", default=False)
@click_insert_context(
    InstanceType.NODE,
    include_name=True,
    include_system_folders=True,
    sandbox_param="sandbox",
)
def cli_node_start(
    ctx: NodeContext,
    name: str,
    system_folders: bool,
    context: str,
    namespace: str,
    attach: bool,
    local_chart_dir: str,
    chart_version: str | None,
) -> None:
    """
    Start the node.
    """
    info("Starting node...")

    prestart_checks(ctx, InstanceType.NODE, name, system_folders)

    k8s_config = select_k8s_config(context=context, namespace=namespace)

    create_directory_if_not_exists(ctx.log_dir)
    create_directory_if_not_exists(ctx.data_dir)
    create_task_namespace_if_not_exists(ctx, k8s_config)

    # Determine image-name. First we check if the image is set in the config file.
    # If so, we use it. Otherwise, we use the same version as HQ.
    if custom_image := ctx.config.get("node", {}).get("image"):
        image = custom_image
    else:
        # if no custom image is specified, find HQ version and use
        # the latest images from that minor version
        client = create_client(ctx, use_sandbox_port=ctx.is_sandbox)
        major_minor = None
        try:
            # try to get HQ version, skip if can't get a connection
            version = client.util.get_hq_version(attempts_on_timeout=3)["version"]
            major_minor = ".".join(version.split(".")[:2])
            image = f"{DEFAULT_NODE_IMAGE_WO_TAG}:{major_minor}"
        except Exception:
            warning("Could not determine HQ version. Using default node image")

        if major_minor and not __version__.startswith(major_minor):
            warning(
                "Version mismatch between CLI and HQ/node. CLI is running on "
                f"version {__version__}, while node and HQ are on version "
                f"{major_minor}. This might cause unexpected issues; changing to "
                f"{major_minor}.<latest> is recommended."
            )

    # fail safe, in case no custom image is specified and we can't get the HQ version
    if not image:
        image = DEFAULT_NODE_IMAGE

    helm_install(
        release_name=ctx.helm_release_name,
        chart_name=ChartName.NODE,
        values_file=ctx.config_file,
        k8s_config=k8s_config,
        local_chart_dir=local_chart_dir,
        custom_values=[f"node.image={image}"],
        chart_version=chart_version,
    )

    if attach:
        attach_logs(
            name,
            instance_type=InstanceType.NODE,
            infra_component=InfraComponentName.NODE,
            system_folders=system_folders,
            k8s_config=k8s_config,
            is_sandbox=ctx.is_sandbox,
        )


def create_task_namespace_if_not_exists(
    ctx: NodeContext, k8s_config: KubernetesConfig
) -> None:
    """
    Create the task namespace if it does not exist.

    Parameters
    ----------
    ctx: NodeContext
        The context of the node.
    k8s_config: KubernetesConfig
        The Kubernetes configuration.
    """
    task_namespace = ctx.config.get("node", {}).get("taskNamespace")
    if not task_namespace:
        warning("Could not find node's task namespace in the config file.")
        exit(1)

    # TODO the namespace creation could use some refactoring, e.g. with v6 use namespace
    core_api = get_core_api_with_ssl_handling()
    try:
        namespace_list = core_api.list_namespace()
    except Exception:
        error(
            "Failed to list namespaces. Check if the cluster is running and reachable."
        )
        exit(1)
    namespace_names = [ns.metadata.name for ns in namespace_list.items]
    if task_namespace not in namespace_names:
        namespace_body = k8s_client.V1Namespace(
            metadata=k8s_client.V1ObjectMeta(name=task_namespace)
        )
        core_api.create_namespace(namespace_body)
