import os

import click
import yaml

from kfk.commands.main import kfk
from kfk.commons import (
    add_kv_config_to_resource,
    create_temp_file,
    delete_last_applied_configuration,
    delete_resource_config,
    get_resource_as_stream,
    open_file_in_system_editor,
    raise_exception_for_missing_options,
)
from kfk.config import STRIMZI_PATH, STRIMZI_VERSION
from kfk.kubectl_command_builder import Kubectl
from kfk.kubernetes_commons import create_using_yaml, delete_using_yaml
from kfk.messages import Messages
from kfk.option_extensions import NotRequiredIf


@click.option("-y", "--yes", "is_yes", help='"Yes" confirmation', is_flag=True)
@click.option(
    "-n",
    "--namespace",
    help="Namespace to use",
    required=True,
    cls=NotRequiredIf,
    options=["is_list"],
)
@click.option(
    "--delete-config",
    help="A cluster configuration override to be removed for an existing cluster",
    multiple=True,
)
@click.option(
    "--config",
    help="A cluster configuration override for the cluster being altered.",
    multiple=True,
)
@click.option("--alter", "is_alter", help="Alters the Kafka cluster.", is_flag=True)
@click.option("--delete", "is_delete", help="Deletes the Kafka cluster.", is_flag=True)
@click.option(
    "--zk-replicas", help="The number of zookeeper replicas for the cluster.", type=int
)
@click.option(
    "--replicas", help="The number of broker replicas for the cluster.", type=int
)
@click.option("--create", "is_create", help="Creates a Kafka cluster.", is_flag=True)
@click.option(
    "--describe",
    "is_describe",
    help="Lists details for the given cluster.",
    is_flag=True,
)
@click.option(
    "-o",
    "--output",
    help=(
        "Output format. One of:"
        " json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath"
        "|jsonpath-file."
    ),
)
@click.option(
    "--list",
    "is_list",
    help="List all available clusters.",
    required=True,
    is_flag=True,
)
@click.option(
    "--cluster",
    help="Cluster Name",
    required=True,
    cls=NotRequiredIf,
    options=["is_list"],
)
@kfk.command()
def clusters(
    cluster,
    is_list,
    is_create,
    replicas,
    zk_replicas,
    is_describe,
    is_delete,
    is_alter,
    config,
    delete_config,
    output,
    namespace,
    is_yes,
):
    """Creates, alters, deletes, describes Kafka cluster(s)."""
    if is_list:
        list(namespace)
    elif is_create:
        create(cluster, replicas, zk_replicas, config, namespace, is_yes)
    elif is_describe:
        describe(cluster, output, namespace)
    elif is_delete:
        delete(cluster, namespace, is_yes)
    elif is_alter:
        alter(cluster, replicas, zk_replicas, config, delete_config, namespace)
    else:
        raise_exception_for_missing_options("clusters")


def list(namespace):
    os.system(Kubectl().get().kafkas().namespace(namespace).build())


def create(cluster, replicas, zk_replicas, config, namespace, is_yes):
    with open(
        "{strimzi_path}/examples/kafka/kafka-ephemeral.yaml".format(
            strimzi_path=STRIMZI_PATH
        ).format(version=STRIMZI_VERSION)
    ) as file:
        stream = file.read()

        cluster_dict = yaml.full_load(stream)

        cluster_dict["metadata"]["name"] = cluster

        _update_replicas(replicas, zk_replicas, cluster_dict)

        _add_config_if_provided(config, cluster_dict)

        cluster_yaml = yaml.dump(cluster_dict)
        cluster_temp_file = create_temp_file(cluster_yaml)

        if is_yes:
            is_confirmed = True
        else:
            open_file_in_system_editor(cluster_temp_file.name)
            is_confirmed = click.confirm(Messages.CLUSTER_CREATE_CONFIRMATION)
        if is_confirmed:
            create_using_yaml(cluster_temp_file.name, namespace)

        cluster_temp_file.close()


def describe(cluster, output, namespace):
    if output is not None:
        os.system(
            Kubectl().get().kafkas(cluster).namespace(namespace).output(output).build()
        )
    else:
        os.system(Kubectl().describe().kafkas(cluster).namespace(namespace).build())


def delete(cluster, namespace, is_yes):
    if is_yes:
        is_confirmed = True
    else:
        is_confirmed = click.confirm(Messages.CLUSTER_DELETE_CONFIRMATION)
    if is_confirmed:
        with open(
            "{strimzi_path}/examples/kafka/kafka-ephemeral.yaml".format(
                strimzi_path=STRIMZI_PATH
            ).format(version=STRIMZI_VERSION)
        ) as file:
            stream = file.read()

            cluster_dict = yaml.full_load(stream)

            cluster_dict["metadata"]["name"] = cluster

            cluster_yaml = yaml.dump(cluster_dict)
            cluster_temp_file = create_temp_file(cluster_yaml)

            delete_using_yaml(cluster_temp_file.name, namespace)

            cluster_temp_file.close()


def alter(cluster, replicas, zk_replicas, config, delete_config, namespace):
    if (
        len(config) > 0
        or len(delete_config) > 0
        or replicas is not None
        or zk_replicas is not None
    ):
        stream = get_resource_as_stream(
            "kafkas", resource_name=cluster, namespace=namespace
        )
        cluster_dict = yaml.full_load(stream)

        delete_last_applied_configuration(cluster_dict)

        _update_replicas(replicas, zk_replicas, cluster_dict)

        _add_config_if_provided(config, cluster_dict)

        if len(delete_config) > 0:
            if cluster_dict["spec"]["kafka"].get("config") is not None:
                delete_resource_config(
                    delete_config, cluster_dict["spec"]["kafka"]["config"]
                )

        cluster_yaml = yaml.dump(cluster_dict)
        cluster_temp_file = create_temp_file(cluster_yaml)
        os.system(
            Kubectl()
            .apply()
            .from_file("{cluster_temp_file_path}")
            .namespace(namespace)
            .build()
            .format(cluster_temp_file_path=cluster_temp_file.name)
        )
        cluster_temp_file.close()
    else:
        os.system(Kubectl().edit().kafkas(cluster).namespace(namespace).build())


def _update_replicas(replicas, zk_replicas, cluster_dict):
    if replicas is not None:
        cluster_dict["spec"]["kafka"]["replicas"] = int(replicas)
        min_insync_replicas = 1
        if replicas > 1:
            min_insync_replicas = replicas - 1
        cluster_dict["spec"]["kafka"]["config"][
            "offsets.topic.replication.factor"
        ] = int(replicas)
        cluster_dict["spec"]["kafka"]["config"][
            "transaction.state.log.replication.factor"
        ] = int(replicas)
        cluster_dict["spec"]["kafka"]["config"]["default.replication.factor"] = int(
            replicas
        )
        cluster_dict["spec"]["kafka"]["config"][
            "transaction.state.log.min.isr"
        ] = min_insync_replicas
        cluster_dict["spec"]["kafka"]["config"][
            "min.insync.replicas"
        ] = min_insync_replicas

    if zk_replicas is not None:
        cluster_dict["spec"]["zookeeper"]["replicas"] = int(zk_replicas)


def _add_config_if_provided(config, cluster_dict):
    if len(config) > 0:
        if cluster_dict["spec"]["kafka"].get("config") is None:
            cluster_dict["spec"]["kafka"]["config"] = {}
        add_kv_config_to_resource(config, cluster_dict["spec"]["kafka"]["config"])
