# -*- coding: utf-8 -*-
#
#   Copyright 2020 Express Systems USA, Inc
#   Copyright 2023 Karellen, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#

import io
import json
import logging
import os
import tempfile
from pathlib import Path
from shutil import which, copy

import yaml

from kubernator.api import (KubernatorPlugin,
                            prepend_os_path,
                            StripNL,
                            get_golang_os,
                            get_golang_machine
                            )

logger = logging.getLogger("kubernator.kubectl")
proc_logger = logger.getChild("proc")
stdout_logger = StripNL(proc_logger.info)
stderr_logger = StripNL(proc_logger.warning)


class KubectlPlugin(KubernatorPlugin):
    logger = logger

    _name = "kubectl"

    def __init__(self):
        self.context = None
        self.kubectl_dir = None
        super().__init__()

    def set_context(self, context):
        self.context = context

    def stanza(self):
        context = self.context.kubectl
        return [context.kubectl_file, f"--kubeconfig={self.context.kubeconfig.kubeconfig}"]

    def register(self,
                 version=None,
                 kubeconfig=None):
        context = self.context

        context.app.register_plugin("kubeconfig")

        if not version and "k8s" in context:
            version = ".".join(context.k8s.server_version)
            logger.info("Kubernetes plugin is running with server version %s - will use it", version)

        if version:
            # Download and use specific version
            kubectl_url = f"https://dl.k8s.io/release/v{version}/bin/" \
                          f"{get_golang_os()}/" \
                          f"{get_golang_machine()}/kubectl"
            kubectl_file_dl, _ = context.app.download_remote_file(logger, kubectl_url, "bin")
            kubectl_file_dl = str(kubectl_file_dl)
            self.kubectl_dir = tempfile.TemporaryDirectory()
            context.app.register_cleanup(self.kubectl_dir)

            kubectl_file = str(Path(self.kubectl_dir.name) / "kubectl")
            copy(kubectl_file_dl, kubectl_file)
            os.chmod(kubectl_file, 0o500)
            prepend_os_path(self.kubectl_dir.name)
        else:
            # Use current version
            kubectl_file = which("kubectl")
            if not kubectl_file:
                raise RuntimeError("`kubectl` cannot be found and no version has been specified")

            logger.debug("Found kubectl in %r", kubectl_file)

        context.globals.kubectl = dict(version=version,
                                       kubectl_file=kubectl_file,
                                       stanza=self.stanza,
                                       test=self.test_kubectl,
                                       run=self.run,
                                       run_capturing=self.run_capturing,
                                       get=self.get,
                                       )

        context.globals.kubectl.version = context.kubectl.test()

    def run_capturing(self, *args, **kwargs):
        return self.context.app.run_capturing_out(self.stanza() +
                                                  list(args),
                                                  stderr_logger,
                                                  **kwargs
                                                  )

    def run(self, *args, **kwargs):
        self.context.app.run(self.stanza() +
                             list(args),
                             stdout_logger,
                             stderr_logger,
                             **kwargs
                             ).wait()

    def get(self, resource_type, resource_name, namespace=None):
        args = ["get", resource_type, resource_name]
        if namespace:
            args += ["-n", namespace]
        args += ["-o", "yaml"]

        res = list(yaml.safe_load_all(io.StringIO(self.context.kubectl.run_capturing(*args))))
        if len(res):
            if len(res) > 1:
                return res
            return res[0]
        return None

    def test_kubectl(self):
        version_out: str = self.run_capturing("version", "--client=true", "-o", "json")

        version_out_js = json.loads(version_out)
        kubectl_version = version_out_js["clientVersion"]["gitVersion"][1:]

        logger.info("Using kubectl %r version %r with stanza %r",
                    self.context.kubectl.kubectl_file, kubectl_version, self.stanza())

        return kubectl_version

    def __repr__(self):
        return "Kubectl Plugin"
