#   Copyright 2012-2013 OpenStack Foundation
#
#   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.
#

"""Manage access to the clients, including authenticating when needed."""

import logging
import pkg_resources
import sys

from keystoneauth1.loading import base
from osc_lib.api import auth
from osc_lib import clientmanager


LOG = logging.getLogger(__name__)

PLUGIN_MODULES = []

USER_AGENT = 'python-openstackclient'


# NOTE(dtroyer): Bringing back select_auth_plugin() and build_auth_params()
#                temporarily because osc-lib 0.3.0 removed it a wee bit early
def select_auth_plugin(options):
    """Pick an auth plugin based on --os-auth-type or other options"""

    auth_plugin_name = None

    # Do the token/url check first as this must override the default
    # 'password' set by os-client-config
    # Also, url and token are not copied into o-c-c's auth dict (yet?)
    if options.auth.get('url') and options.auth.get('token'):
        # service token authentication
        auth_plugin_name = 'token_endpoint'
    elif options.auth_type in auth.PLUGIN_LIST:
        # A direct plugin name was given, use it
        auth_plugin_name = options.auth_type
    elif options.auth.get('username'):
        if options.identity_api_version == '3':
            auth_plugin_name = 'v3password'
        elif options.identity_api_version.startswith('2'):
            auth_plugin_name = 'v2password'
        else:
            # let keystoneauth figure it out itself
            auth_plugin_name = 'password'
    elif options.auth.get('token'):
        if options.identity_api_version == '3':
            auth_plugin_name = 'v3token'
        elif options.identity_api_version.startswith('2'):
            auth_plugin_name = 'v2token'
        else:
            # let keystoneauth figure it out itself
            auth_plugin_name = 'token'
    else:
        # The ultimate default is similar to the original behaviour,
        # but this time with version discovery
        auth_plugin_name = 'password'
    LOG.debug("Auth plugin %s selected", auth_plugin_name)
    return auth_plugin_name


def build_auth_params(auth_plugin_name, cmd_options):
    if auth_plugin_name:
        LOG.debug('auth_type: %s', auth_plugin_name)
        auth_plugin_loader = base.get_plugin_loader(auth_plugin_name)
        auth_params = {
            opt.dest: opt.default
            for opt in base.get_plugin_options(auth_plugin_name)
        }
        auth_params.update(dict(cmd_options.auth))
        # grab tenant from project for v2.0 API compatibility
        if auth_plugin_name.startswith("v2"):
            if 'project_id' in auth_params:
                auth_params['tenant_id'] = auth_params['project_id']
                del auth_params['project_id']
            if 'project_name' in auth_params:
                auth_params['tenant_name'] = auth_params['project_name']
                del auth_params['project_name']
    else:
        LOG.debug('no auth_type')
        # delay the plugin choice, grab every option
        auth_plugin_loader = None
        auth_params = dict(cmd_options.auth)
        plugin_options = set(
            [o.replace('-', '_') for o in auth.get_options_list()]
        )
        for option in plugin_options:
            LOG.debug('fetching option %s', option)
            auth_params[option] = getattr(cmd_options.auth, option, None)
    return (auth_plugin_loader, auth_params)


class ClientManager(clientmanager.ClientManager):
    """Manages access to API clients, including authentication

    Wrap osc_lib's ClientManager to maintain compatibility for the existing
    plugin V2 interface.  Some currently private attributes become public
    in osc-lib so we need to maintain a transition period.
    """

    # A simple incrementing version for the plugin to know what is available
    PLUGIN_INTERFACE_VERSION = "2"

    def __init__(
        self,
        cli_options=None,
        api_version=None,
        pw_func=None,
    ):
        super(ClientManager, self).__init__(
            cli_options=cli_options,
            api_version=api_version,
            pw_func=pw_func,
        )

        # TODO(dtroyer): For compatibility; mark this for removal when plugin
        #                interface v2 is removed
        self._region_name = self.region_name
        self._interface = self.interface
        self._cacert = self.cacert
        self._insecure = not self.verify

    def is_network_endpoint_enabled(self):
        """Check if the network endpoint is enabled"""

        # NOTE(dtroyer): is_service_available() can also return None if
        #                there is no Service Catalog, callers here are
        #                not expecting that so fold None into True to
        #                use Network API by default
        return self.is_service_available('network') is not False


# Plugin Support

def get_plugin_modules(group):
    """Find plugin entry points"""
    mod_list = []
    for ep in pkg_resources.iter_entry_points(group):
        LOG.debug('Found plugin %r', ep.name)

        __import__(ep.module_name)
        module = sys.modules[ep.module_name]
        mod_list.append(module)
        init_func = getattr(module, 'Initialize', None)
        if init_func:
            init_func('x')

        # Add the plugin to the ClientManager
        setattr(
            ClientManager,
            module.API_NAME,
            clientmanager.ClientCache(
                getattr(sys.modules[ep.module_name], 'make_client', None)
            ),
        )
    return mod_list


def build_plugin_option_parser(parser):
    """Add plugin options to the parser"""

    # Loop through extensions to get parser additions
    for mod in PLUGIN_MODULES:
        parser = mod.build_option_parser(parser)
    return parser


# Get list of base plugin modules
PLUGIN_MODULES = get_plugin_modules(
    'openstack.cli.base',
)
# Append list of external plugin modules
PLUGIN_MODULES.extend(get_plugin_modules(
    'openstack.cli.extension',
))