# Copyright (C) 2012 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.

from gettext import gettext as _

from pulp.client import arg_utils, parsers
from pulp.client.commands import options
from pulp.client.commands.repo.importer_config import ImporterConfigMixin
from pulp.client.extensions.extensions import PulpCliOption
from pulp.client.commands.repo.cudl import CreateRepositoryCommand, ListRepositoriesCommand, UpdateRepositoryCommand

from pulp_puppet.common import constants


DESC_FEED = _('URL of the external source from which to import Puppet modules')
OPTION_FEED = PulpCliOption('--feed', DESC_FEED, required=False)

DESC_QUERY = _(
    '(deprecated) ignored if "--queries" is specified. '
    'query to issue against the feed\'s modules.json file to scope which '
    'modules are imported; multiple queries may be added by specifying this '
    'argument multiple times'
)
OPTION_QUERY = PulpCliOption('--query', DESC_QUERY, required=False, allow_multiple=True)

DESC_QUERIES = _(
    'comma-separated list of queries to issue against the feed\'s modules.json '
    'file to scope which modules are imported.'
)
OPTION_QUERIES = PulpCliOption(
    '--queries', DESC_QUERIES, required=False, allow_multiple=False,
    parse_func=parsers.csv)

DESC_QUERIES_UPDATE = _(
    'comma-separated list of queries to issue against the feed\'s modules.json '
    'file to scope which modules are imported. overwrites previous values.'
)
OPTION_QUERIES_UPDATE = PulpCliOption(
    '--queries', DESC_QUERIES_UPDATE, required=False, allow_multiple=False,
    parse_func=parsers.csv)

DESC_HTTP = _('if "true", the repository will be served over HTTP; defaults to true')
OPTION_HTTP = PulpCliOption('--serve-http', DESC_HTTP, required=False)

DESC_HTTPS = _('if "true", the repository will be served over HTTPS; defaults to false')
OPTION_HTTPS = PulpCliOption('--serve-https', DESC_HTTPS, required=False)

DESC_SEARCH = _('searches for Puppet repositories on the server')


class CreatePuppetRepositoryCommand(CreateRepositoryCommand, ImporterConfigMixin):
    def __init__(self, context):
        CreateRepositoryCommand.__init__(self, context)
        ImporterConfigMixin.__init__(self, include_unit_policy=False)

        self.add_option(OPTION_QUERIES)
        self.add_option(OPTION_QUERY)
        self.add_option(OPTION_HTTP)
        self.add_option(OPTION_HTTPS)

    def run(self, **kwargs):

        # -- repository metadata --
        repo_id = kwargs[options.OPTION_REPO_ID.keyword]
        description = kwargs[options.OPTION_DESCRIPTION.keyword]
        notes = kwargs.pop(options.OPTION_NOTES.keyword) or {}

        # Add a note to indicate this is a Puppet repository
        notes[constants.REPO_NOTE_KEY] = constants.REPO_NOTE_PUPPET

        name = repo_id
        if options.OPTION_NAME.keyword in kwargs:
            name = kwargs[options.OPTION_NAME.keyword]

        # -- importer metadata --
        importer_config = self.parse_user_input(kwargs)
        importer_config.update({constants.CONFIG_QUERIES: kwargs[OPTION_QUERIES.keyword] or kwargs[OPTION_QUERY.keyword]})
        arg_utils.convert_removed_options(importer_config)

        # -- distributor metadata --
        distributor_config = {
            constants.CONFIG_SERVE_HTTP : kwargs[OPTION_HTTP.keyword],
            constants.CONFIG_SERVE_HTTPS : kwargs[OPTION_HTTPS.keyword],
            }
        arg_utils.convert_removed_options(distributor_config)
        arg_utils.convert_boolean_arguments((constants.CONFIG_SERVE_HTTP, constants.CONFIG_SERVE_HTTPS), distributor_config)

        distributors = [
            dict(distributor_type=constants.DISTRIBUTOR_TYPE_ID, distributor_config=distributor_config,
                 auto_publish=True, distributor_id=constants.DISTRIBUTOR_ID)
        ]

        # Create the repository
        self.context.server.repo.create_and_configure(repo_id, name, description,
            notes, constants.IMPORTER_TYPE_ID, importer_config, distributors)

        msg = _('Successfully created repository [%(r)s]')
        self.context.prompt.render_success_message(msg % {'r' : repo_id})


class UpdatePuppetRepositoryCommand(UpdateRepositoryCommand, ImporterConfigMixin):

    def __init__(self, context):
        UpdateRepositoryCommand.__init__(self, context)
        ImporterConfigMixin.__init__(self, include_unit_policy=False)

        self.add_option(OPTION_QUERIES_UPDATE)
        self.add_option(OPTION_QUERY)
        self.add_option(OPTION_HTTP)
        self.add_option(OPTION_HTTPS)

    def run(self, **kwargs):
        # -- repository metadata --
        repo_id = kwargs.pop(options.OPTION_REPO_ID.keyword)
        description = kwargs.pop(options.OPTION_DESCRIPTION.keyword, None)
        name = kwargs.pop(options.OPTION_NAME.keyword, None)
        notes = kwargs.pop(options.OPTION_NOTES.keyword, None)

        # -- importer metadata --
        queries = kwargs.pop(OPTION_QUERIES.keyword, None)
        if queries is None:
            queries = kwargs.pop(OPTION_QUERY.keyword, None)
        importer_config = self.parse_user_input(kwargs)
        importer_config.update({constants.CONFIG_QUERIES: queries})
        arg_utils.convert_removed_options(importer_config)

        # -- distributor metadata --
        distributor_config = {
            constants.CONFIG_SERVE_HTTP : kwargs.pop(OPTION_HTTP.keyword, None),
            constants.CONFIG_SERVE_HTTPS : kwargs.pop(OPTION_HTTPS.keyword, None),
            }
        arg_utils.convert_removed_options(distributor_config)
        arg_utils.convert_boolean_arguments((constants.CONFIG_SERVE_HTTP, constants.CONFIG_SERVE_HTTPS), distributor_config)

        distributor_configs = {constants.DISTRIBUTOR_ID : distributor_config}

        # -- server update --
        response = self.context.server.repo.update_repo_and_plugins(repo_id, name,
            description, notes, importer_config, distributor_configs)

        if not response.is_async():
            msg = _('Repository [%(r)s] successfully updated')
            self.context.prompt.render_success_message(msg % {'r' : repo_id})
        else:
            d = _('Repository update postponed due to another operation. Progress '
                  'on this task can be viewed using the commands under "repo tasks".')
            self.context.prompt.render_paragraph(d, tag='postponed')
            self.context.prompt.render_reasons(response.response_body.reasons)


class ListPuppetRepositoriesCommand(ListRepositoriesCommand):

    def __init__(self, context):
        repos_title = _('Puppet Repositories')
        super(ListPuppetRepositoriesCommand, self).__init__(context,
                                                            repos_title=repos_title)

        # Both get_repositories and get_other_repositories will act on the full
        # list of repositories. Lazy cache the data here since both will be
        # called in succession, saving the round trip to the server.
        self.all_repos_cache = None

    def get_repositories(self, query_params, **kwargs):
        all_repos = self._all_repos(query_params, **kwargs)

        puppet_repos = []
        for repo in all_repos:
            notes = repo['notes']
            if constants.REPO_NOTE_KEY in notes and notes[constants.REPO_NOTE_KEY] == constants.REPO_NOTE_PUPPET:
                puppet_repos.append(repo)

        for repo in puppet_repos:
            if repo.get('distributors'):
                repo['distributors'][0]['relative_path'] = 'puppet/%s/' % repo['id']

        return puppet_repos

    def get_other_repositories(self, query_params, **kwargs):
        all_repos = self._all_repos(query_params, **kwargs)

        non_puppet_repos = []
        for repo in all_repos:
            notes = repo['notes']
            if notes.get(constants.REPO_NOTE_KEY, None) != constants.REPO_NOTE_PUPPET:
                non_puppet_repos.append(repo)

        return non_puppet_repos

    def _all_repos(self, query_params, **kwargs):

        # This is safe from any issues with concurrency due to how the CLI works
        if self.all_repos_cache is None:
            self.all_repos_cache = self.context.server.repo.repositories(query_params).response_body

        return self.all_repos_cache

