import json
import subprocess
from pathlib import Path

import questionary as q

from vantage6.common import error
from vantage6.common.globals import (
    APPNAME,
    SANDBOX_SUFFIX,
    InstanceType,
)

from vantage6.cli.config import CliConfig
from vantage6.cli.globals import CLICommandName
from vantage6.cli.utils import validate_input_cmd_args


def select_context_and_namespace(
    context: str | None = None,
    namespace: str | None = None,
) -> tuple[str, str]:
    """
    Select the context and namespace to use.

    This uses the CLI config to compare the provided context and namespace with the
    last used context and namespace. If the provided context and namespace are not
    the same as the last used context and namespace, the CLI config is updated.

    Parameters
    ----------
    context : str, optional
        The Kubernetes context to use.
    namespace : str, optional
        The Kubernetes namespace to use.

    Returns
    -------
    tuple[str, str]
        The context and namespace to use
    """
    cli_config = CliConfig()

    return cli_config.compare_changes_config(
        context=context,
        namespace=namespace,
    )


def create_directory_if_not_exists(directory: Path) -> None:
    """
    Create a directory.
    """
    try:
        directory.mkdir(parents=True, exist_ok=True)
    except Exception as e:
        error(f"Failed to create directory {directory}: {e}")
        exit(1)


def find_running_service_names(
    instance_type: InstanceType,
    only_system_folders: bool = False,
    only_user_folders: bool = False,
    context: str | None = None,
    namespace: str | None = None,
    sandbox: bool = False,
) -> list[str]:
    """
    List running Vantage6 servers.

    Parameters
    ----------
    instance_type : InstanceType
        The type of instance to find running services for
    only_system_folders : bool, optional
        Whether to look for system-based services or not. By default False.
    only_user_folders : bool, optional
        Whether to look for user-based services or not. By default False.
    context : str, optional
        The Kubernetes context to use.
    namespace : str, optional
        The Kubernetes namespace to use.
    sandbox : bool, optional
        Whether to look for sandbox services or not. By default False.

    Returns
    -------
    list[str]
        List of release names that are running
    """
    # Input validation
    validate_input_cmd_args(context, "context name", allow_none=True)
    validate_input_cmd_args(namespace, "namespace name", allow_none=True)
    validate_input_cmd_args(instance_type, "instance type", allow_none=False)
    if only_system_folders and only_user_folders:
        error("Cannot use both only_system_folders and only_user_folders")
        exit(1)

    # Create the command
    command = [
        "helm",
        "list",
        "--output",
        "json",  # Get structured output
    ]

    if context:
        command.extend(["--kube-context", context])

    if namespace:
        command.extend(["--namespace", namespace])
    else:
        command.extend(["--all-namespaces"])

    try:
        result = subprocess.run(
            command,
            capture_output=True,
            text=True,
            check=True,
        )
    except subprocess.CalledProcessError as e:
        error(f"Failed to list Helm releases: {e}")
        return []
    except FileNotFoundError:
        error(
            "Helm command not found. Please ensure Helm is installed and available in "
            "the PATH."
        )
        return []

    try:
        releases = json.loads(result.stdout)
    except json.JSONDecodeError:
        error("Failed to parse Helm output as JSON")
        return []

    # filter services for the vantage6 services that are sought. These have
    # the following pattern:
    # f"{APPNAME}-{name}-{scope}-{instance_type.value}"

    # filter for the instance type
    svc_starts_with = f"{APPNAME}-"
    if only_system_folders:
        svc_ends_with = f"system-{instance_type.value}"
    elif only_user_folders:
        svc_ends_with = f"user-{instance_type.value}"
    else:
        svc_ends_with = f"-{instance_type.value}"

    matching_services = []
    for release in releases:
        release_name = release.get("name", "")

        # Check if this is a Vantage6 server release
        is_matching_service = (
            release_name.startswith(svc_starts_with)
            and release_name.endswith(svc_ends_with)
            and instance_type.value in release_name
        )

        if is_matching_service:
            matching_services.append(release_name)

    return matching_services


def select_running_service(
    running_services: list[str],
    instance_type: InstanceType,
) -> str:
    """
    Select a running service from the list of running services.
    """
    try:
        name = q.select(
            f"Select a {instance_type.value}:",
            choices=running_services,
        ).unsafe_ask()
    except KeyboardInterrupt:
        error("Aborted by user!")
        exit(1)
    return name


def get_config_name_from_service_name(service_name: str) -> str:
    """
    Get the config name from a service name.
    """
    # helm release name is structured as:
    # f"{APPNAME}-{name}-{scope}-{instance_type}"
    # we want to get the name from the service name
    return "-".join(service_name.split("-")[1:-2])


def get_main_cli_command_name(instance_type: InstanceType) -> str:
    """
    Get the main CLI command name for a given instance type.

    Parameters
    ----------
    instance_type : InstanceType
        The type of instance to get the main CLI command name for
    """
    if instance_type == InstanceType.SERVER:
        return CLICommandName.SERVER.value
    elif instance_type == InstanceType.ALGORITHM_STORE:
        return CLICommandName.ALGORITHM_STORE.value
    elif instance_type == InstanceType.NODE:
        return CLICommandName.NODE.value
    elif instance_type == InstanceType.AUTH:
        return CLICommandName.AUTH.value
    else:
        raise ValueError(f"Invalid instance type: {instance_type}")


def check_running(
    helm_release_name: str, instance_type: InstanceType, name: str, system_folders: bool
) -> bool:
    """
    Check if the instance is already running.

    Parameters
    ----------
    helm_release_name : str
        The name of the Helm release.
    instance_type : InstanceType
        The type of instance to check
    name : str
        The name of the instance to check
    system_folders : bool
        Whether to use system folders or not

    Returns
    -------
    bool
        True if the instance is already running, False otherwise
    """
    running_services = find_running_service_names(
        instance_type=instance_type,
        only_system_folders=system_folders,
        only_user_folders=not system_folders,
    )
    return helm_release_name in running_services


def get_config_name_from_helm_release_name(
    helm_release_name: str, is_store: bool = False
) -> str:
    """
    Get the config name from a helm release name.

    Parameters
    ----------
    helm_release_name : str
        The name of the Helm release
    is_store : bool, optional
        Whether the instance is a store or not. By default False.

    Returns
    -------
    str
        The config name
    """
    # helm release name is structured as:
    # f"{APPNAME}-{name}-{scope}-{instance_type}"
    # we want to get the name from the service name
    if is_store:
        # for store, the instance type is `algorithm-store` which contains an additional
        # hyphen
        return "-".join(helm_release_name.split("-")[1:-3])
    else:
        return "-".join(helm_release_name.split("-")[1:-2])


def extract_name_and_is_sandbox(name: str | None, is_sandbox: bool) -> tuple[str, bool]:
    """
    Extract the name and is_sandbox from the name.

    Note that the name may be None: this occurs before when this function is called
    before the user has selected a name. This scenario is fine because when the user
    selects a name interactively, the name never ends with the .sandbox suffix.

    Parameters
    ----------
    name : str | None
        The name of the instance
    is_sandbox : bool
        Whether the instance is a sandbox instance

    Returns
    -------
    tuple[str, bool]
        The name and is_sandbox
    """
    if name and name.endswith(SANDBOX_SUFFIX):
        return name[: -len(SANDBOX_SUFFIX)], True
    else:
        return name, is_sandbox
