# -*- coding: utf-8 -*-
#
# 2016 Darko Poljak (darko.poljak at ungleich.ch)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
#

import cdist
import cdist.config
import cdist.core
import cdist.preos
import argparse
import cdist.argparse
import logging
import os
import subprocess


class Debian:
    _preos_name = 'debian'
    _cdist_preos = True

    _files_dir = os.path.join(os.path.dirname(__file__), "files")

    @classmethod
    def default_args(cls):
        default_remote_exec = os.path.join(cls._files_dir, "remote-exec.sh")
        default_remote_copy = os.path.join(cls._files_dir, "remote-copy.sh")
        default_init_manifest = os.path.join(
            cls._files_dir, "init-manifest-{}".format(cls._preos_name))

        defargs = argparse.Namespace()
        defargs.arch = 'amd64'
        defargs.bootstrap = False
        defargs.configure = False
        defargs.cdist_params = '-v'
        defargs.rm_bootstrap_dir = False
        defargs.suite = 'stable'
        defargs.remote_exec = default_remote_exec
        defargs.remote_copy = default_remote_copy
        defargs.manifest = default_init_manifest

        return defargs

    @classmethod
    def get_parser(cls):
        defargs = cls.default_args()
        cdist_parser = cdist.argparse.get_parsers()
        parser = argparse.ArgumentParser(
                prog='cdist preos {}'.format(cls._preos_name),
                parents=[cdist_parser['loglevel'], cdist_parser['beta']])
        parser.add_argument('target_dir', nargs=1,
                            help=("target directory where PreOS will be "
                                  "bootstrapped"))
        parser.add_argument(
            '-a', '--arch',
            help="target debootstrap architecture, by default '{}'".format(
                defargs.arch), dest='arch', default=defargs.arch)
        parser.add_argument(
            '-B', '--bootstrap',
            help='do bootstrap step',
            dest='bootstrap', action='store_true', default=defargs.bootstrap)
        parser.add_argument(
            '-C', '--configure',
            help='do configure step',
            dest='configure', action='store_true', default=defargs.configure)
        parser.add_argument(
            '-c', '--cdist-params',
            help=("parameters that will be passed to cdist config, by default"
                  " '{}' is used".format(defargs.cdist_params)),
            dest='cdist_params', default=defargs.cdist_params)
        parser.add_argument(
            '-D', '--drive-boot',
            help='create bootable PreOS on specified drive',
            dest='drive')
        parser.add_argument(
            '-e', '--remote-exec',
            help=("remote exec that cdist config will use, by default "
                  "internal script is used"),
            dest='remote_exec', default=defargs.remote_exec)
        parser.add_argument(
            '-i', '--init-manifest',
            help=("init manifest that cdist config will use, by default "
                  "internal init manifest is used"),
            dest='manifest', default=defargs.manifest)
        parser.add_argument(
            '-k', '--keyfile', action="append",
            help=("ssh key files that will be added to cdist config; "
                  "'__ssh_authorized_keys root ...' type is appended to "
                  "initial manifest"),
            dest='keyfile')
        parser.add_argument(
            '-m', '--mirror',
            help='use specified mirror for debootstrap',
            dest='mirror')
        parser.add_argument(
            '-P', '--root-password',
            help='Set specified password for root, generated by default',
            dest='root_password')
        parser.add_argument('-p', '--pxe-boot-dir', help='PXE boot directory',
                            dest='pxe_boot_dir')
        parser.add_argument(
            '-r', '--rm-bootstrap-dir',
            help='remove target directory after finishing',
            dest='rm_bootstrap_dir', action='store_true',
            default=defargs.rm_bootstrap_dir)
        parser.add_argument(
            '-S', '--script',
            help='use specified script for debootstrap',
            dest='script')
        parser.add_argument('-s', '--suite',
                            help="suite used for debootstrap, "
                                 "by default '{}'".format(defargs.suite),
                            dest='suite', default=defargs.suite)
        parser.add_argument(
            '-y', '--remote-copy',
            help=("remote copy that cdist config will use, by default "
                  "internal script is used"),
            dest='remote_copy', default=defargs.remote_copy)
        parser.epilog = cdist.argparse.EPILOG

        return parser

    @classmethod
    def update_env(cls, env):
        pass

    @classmethod
    def commandline(cls, argv):
        log = logging.getLogger(cls.__name__)

        parser = cls.get_parser()
        args = parser.parse_args(argv)
        if args.script and not args.mirror:
            raise cdist.Error("script option cannot be used without "
                              "mirror option")

        args.command = cls._preos_name
        cdist.argparse.check_beta(vars(args))

        cdist.preos.check_root()

        args.target_dir = os.path.realpath(args.target_dir[0])
        args.os = cls._preos_name
        args.remote_exec = os.path.realpath(args.remote_exec)
        args.remote_copy = os.path.realpath(args.remote_copy)
        args.manifest = os.path.realpath(args.manifest)
        if args.keyfile:
            new_keyfile = [os.path.realpath(x) for x in args.keyfile]
            args.keyfile = new_keyfile
        if args.pxe_boot_dir:
            args.pxe_boot_dir = os.path.realpath(args.pxe_boot_dir)

        cdist.argparse.handle_loglevel(args)
        log.debug("preos: %s, args: %s", cls._preos_name, args)
        try:
            env = vars(args)
            new_env = {}
            for key in env:
                if key == 'verbose' and env[key]:
                    if env[key] >= 3:
                        new_env['debug'] = "yes"
                    elif env[key] == 2:
                        new_env['verbose'] = "yes"
                elif not env[key]:
                    new_env[key] = ''
                elif isinstance(env[key], bool) and env[key]:
                    new_env[key] = "yes"
                elif isinstance(env[key], list):
                    val = env[key]
                    new_env[key + "_cnt"] = str(len(val))
                    for i, v in enumerate(val):
                        new_env[key + "_" + str(i)] = v
                else:
                    new_env[key] = str(env[key])
            env = new_env
            env.update(os.environ)
            cls.update_env(env)
            log.debug("preos: %s env: %s", cls._preos_name, env)

            if log.getEffectiveLevel() <= logging.INFO:
                info_msg = ["Running preos: {}, suite: {}, arch: {}".format(
                    cls._preos_name, args.suite, args.arch), ]
                if args.mirror:
                    info_msg.append("mirror: {}".format(args.mirror))
                if args.script:
                    info_msg.append("script: {}".format(args.script))
                if args.bootstrap:
                    info_msg.append("bootstrapping")
                if args.configure:
                    info_msg.append("configuring")
                if args.pxe_boot_dir:
                    info_msg.append("creating PXE")
                if args.drive:
                    info_msg.append("creating bootable drive")
                log.info(info_msg)

            cmd = os.path.join(cls._files_dir, "code")
            log.debug("cmd=%s", cmd)
            subprocess.check_call(cmd, env=env, shell=True)
        except subprocess.CalledProcessError as e:
            log.error("preos %s failed: %s", cls._preos_name, e)


class Ubuntu(Debian):
    _preos_name = "ubuntu"

    @classmethod
    def default_args(cls):
        defargs = super().default_args()
        defargs.suite = 'xenial'
        return defargs


class Devuan(Debian):
    _preos_name = "devuan"

    @classmethod
    def default_args(cls):
        defargs = super().default_args()
        defargs.suite = 'jessie'
        return defargs

    @classmethod
    def update_env(cls, env):
        env['DEBOOTSTRAP_DIR'] = os.path.join(cls._files_dir,
                                              'devuan-debootstrap')
