from __future__ import print_function
import argparse
import os
import pkg_resources
import sys
import logging

from ceph_volume.decorators import catches
from ceph_volume import log, devices, configuration, conf, exceptions, terminal, inventory, drive_group


class Volume(object):
    _help = """
ceph-volume: Deploy Ceph OSDs using different device technologies like lvm or
physical disks.

Log Path: {log_path}
Ceph Conf: {ceph_path}

{sub_help}
{plugins}
{environ_vars}
{warning}
    """

    def __init__(self, argv=None, parse=True):
        self.mapper = {
            'lvm': devices.lvm.LVM,
            'simple': devices.simple.Simple,
            'raw': devices.raw.Raw,
            'inventory': inventory.Inventory,
            'drive-group': drive_group.Deploy,
        }
        self.plugin_help = "No plugins found/loaded"
        if argv is None:
            self.argv = sys.argv
        else:
            self.argv = argv
        if parse:
            self.main(self.argv)

    def help(self, warning=False):
        warning = 'See "ceph-volume --help" for full list of options.' if warning else ''
        return self._help.format(
            warning=warning,
            log_path=conf.log_path,
            ceph_path=self.stat_ceph_conf(),
            plugins=self.plugin_help,
            sub_help=terminal.subhelp(self.mapper),
            environ_vars=self.get_environ_vars()
        )

    def get_environ_vars(self):
        environ_vars = []
        for key, value in os.environ.items():
            if key.startswith('CEPH_'):
                environ_vars.append("%s=%s" % (key, value))
        if not environ_vars:
            return ''
        else:
            environ_vars.insert(0, '\nEnviron Variables:')
            return '\n'.join(environ_vars)

    def enable_plugins(self):
        """
        Load all plugins available, add them to the mapper and extend the help
        string with the information from each one
        """
        plugins = _load_library_extensions()
        for plugin in plugins:
            self.mapper[plugin._ceph_volume_name_] = plugin
        self.plugin_help = '\n'.join(['%-19s %s\n' % (
            plugin.name, getattr(plugin, 'help_menu', ''))
            for plugin in plugins])
        if self.plugin_help:
            self.plugin_help = '\nPlugins:\n' + self.plugin_help

    def load_log_path(self):
        conf.log_path = os.getenv('CEPH_VOLUME_LOG_PATH', '/var/log/ceph')

    def stat_ceph_conf(self):
        try:
            configuration.load(conf.path)
            return terminal.green(conf.path)
        except exceptions.ConfigurationError as error:
            return terminal.red(error)

    def _get_split_args(self):
        subcommands = self.mapper.keys()
        slice_on_index = len(self.argv) + 1
        pruned_args = self.argv[1:]
        for count, arg in enumerate(pruned_args):
            if arg in subcommands:
                slice_on_index = count
                break
        return pruned_args[:slice_on_index], pruned_args[slice_on_index:]

    @catches()
    def main(self, argv):
        # these need to be available for the help, which gets parsed super
        # early
        configuration.load_ceph_conf_path()
        self.load_log_path()
        self.enable_plugins()
        main_args, subcommand_args = self._get_split_args()
        # no flags where passed in, return the help menu instead of waiting for
        # argparse which will end up complaning that there are no args
        if len(argv) <= 1:
            print(self.help(warning=True))
            raise SystemExit(0)
        parser = argparse.ArgumentParser(
            prog='ceph-volume',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=self.help(),
        )
        parser.add_argument(
            '--cluster',
            default='ceph',
            help='Cluster name (defaults to "ceph")',
        )
        parser.add_argument(
            '--log-level',
            default='debug',
            choices=['debug', 'info', 'warning', 'error', 'critical'],
            help='Change the file log level (defaults to debug)',
        )
        parser.add_argument(
            '--log-path',
            default='/var/log/ceph/',
            help='Change the log path (defaults to /var/log/ceph)',
        )
        args = parser.parse_args(main_args)
        conf.log_path = args.log_path
        if os.path.isdir(conf.log_path):
            conf.log_path = os.path.join(args.log_path, 'ceph-volume.log')
        log.setup(log_level=args.log_level)
        log.setup_console()
        logger = logging.getLogger(__name__)
        logger.info("Running command: ceph-volume %s %s", " ".join(main_args), " ".join(subcommand_args))
        # set all variables from args and load everything needed according to
        # them
        configuration.load_ceph_conf_path(cluster_name=args.cluster)
        try:
            conf.ceph = configuration.load(conf.path)
        except exceptions.ConfigurationError as error:
            # we warn only here, because it is possible that the configuration
            # file is not needed, or that it will be loaded by some other means
            # (like reading from lvm tags)
            logger.exception('ignoring inability to load ceph.conf')
            terminal.red(error)
        # dispatch to sub-commands
        terminal.dispatch(self.mapper, subcommand_args)


def _load_library_extensions():
    """
    Locate all setuptools entry points by the name 'ceph_volume_handlers'
    and initialize them.
    Any third-party library may register an entry point by adding the
    following to their setup.py::

        entry_points = {
            'ceph_volume_handlers': [
                'plugin_name = mylib.mymodule:Handler_Class',
            ],
        },

    `plugin_name` will be used to load it as a sub command.
    """
    logger = logging.getLogger('ceph_volume.plugins')
    group = 'ceph_volume_handlers'
    entry_points = pkg_resources.iter_entry_points(group=group)
    plugins = []
    for ep in entry_points:
        try:
            logger.debug('loading %s' % ep.name)
            plugin = ep.load()
            plugin._ceph_volume_name_ = ep.name
            plugins.append(plugin)
        except Exception as error:
            logger.exception("Error initializing plugin %s: %s" % (ep, error))
    return plugins
