# -*- coding: utf-8 -*-
#
# 2017 Darko Poljak (darko.poljak at gmail.com)
#
# 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
# needed for cdist.argparse
import cdist.banner
import cdist.config
import cdist.install
import cdist.shell
import cdist.inventory
import cdist.argparse
import cdist.log
import cdist.config
import cdist.install
import sys
import os
import os.path
import collections
import uuid
import shutil


def find_cdist_exec_in_path():
    """Search cdist executable in os.get_exec_path() entries.
    """
    for path in os.get_exec_path():
        cdist_path = os.path.join(path, 'cdist')
        if os.access(cdist_path, os.X_OK):
            return cdist_path
    return None


_mydir = os.path.dirname(__file__)


def find_cdist_exec():
    """Search cdist executable starting from local lib directory.

    Detect if ../bin/cdist (from local lib directory) exists and
    if it is executable. If not then try to find cdist exec path in
    os.get_exec_path() entries. If no cdist path is found rasie
    cdist.Error.
    """
    cdist_path = os.path.abspath(os.path.join(_mydir, '..', 'bin', 'cdist'))
    if os.access(cdist_path, os.X_OK):
        return cdist_path
    cdist_path = find_cdist_exec_in_path()
    if not cdist_path:
        raise cdist.Error('Cannot find cdist executable from local lib '
                          'directory: {}, nor in PATH: {}.'.format(
                              _mydir, os.environ.get('PATH')))
    return cdist_path


ACTION_CONFIG = 'config'
ACTION_INSTALL = 'install'


def _process_hosts_simple(action, host, manifest, verbose,
                          cdist_path=None):
    """Perform cdist action ('config' or 'install') on hosts with specified
       manifest using default other cdist options. host parameter can be a
       string or iterbale of hosts. verbose is a desired verbosity level
       which defaults to VERBOSE_INFO. cdist_path is path to cdist executable,
       if it is None then integration lib tries to find it.
    """
    if isinstance(host, str):
        hosts = [host, ]
    elif isinstance(host, collections.abc.Iterable):
        hosts = host
    else:
        raise cdist.Error('Invalid host argument: {}'.format(host))

    # Setup sys.argv[0] since cdist relies on command line invocation.
    if not cdist_path:
        cdist_path = find_cdist_exec()
    sys.argv[0] = cdist_path

    cname = action.title()
    module = getattr(cdist, action)
    theclass = getattr(module, cname)

    # Build argv for cdist and use argparse for argument parsing.
    remote_out_dir_base = os.path.join('/', 'var', 'lib', 'cdist')
    uid = str(uuid.uuid1())
    out_dir = remote_out_dir_base + uid
    cache_path_pattern = '%h-' + uid
    argv = [action, '-i', manifest, '-r', out_dir, '-C', cache_path_pattern, ]
    for i in range(verbose):
        argv.append('-v')
    for x in hosts:
        argv.append(x)

    parser, cfg = cdist.argparse.parse_and_configure(argv, singleton=False)
    args = cfg.get_args()
    configuration = cfg.get_config(section='GLOBAL')

    theclass.construct_remote_exec_copy_patterns(args)
    base_root_path = theclass.create_base_root_path(None)

    for target_host in args.host:
        host_base_path, hostdir = theclass.create_host_base_dirs(
            target_host, base_root_path)
        theclass.onehost(target_host, None, host_base_path, hostdir, args,
                         parallel=False, configuration=configuration,
                         remove_remote_files_dirs=True)
    shutil.rmtree(base_root_path)


def configure_hosts_simple(host, manifest,
                           verbose=cdist.argparse.VERBOSE_INFO,
                           cdist_path=None):
    """Configure hosts with specified manifest using default other cdist
       options. host parameter can be a string or iterbale of hosts. verbose
       is a desired verbosity level which defaults to VERBOSE_INFO.
       cdist_path is path to cdist executable, if it is None then integration
       lib tries to find it.
    """
    _process_hosts_simple(action=ACTION_CONFIG, host=host,
                          manifest=manifest, verbose=verbose,
                          cdist_path=cdist_path)


def install_hosts_simple(host, manifest,
                         verbose=cdist.argparse.VERBOSE_INFO,
                         cdist_path=None):
    """Install hosts with specified manifest using default other cdist
       options. host parameter can be a string or iterbale of hosts. verbose
       is a desired verbosity level which defaults to VERBOSE_INFO.
       cdist_path is path to cdist executable, if it is None then integration
       lib tries to find it.
    """
    _process_hosts_simple(action=ACTION_INSTALL, host=host,
                          manifest=manifest, verbose=verbose,
                          cdist_path=cdist_path)
