# Copyright (c) 2013 Mirantis Inc.
#
# 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.

from keystoneclient import exceptions as keystone_ex
from keystoneclient import service_catalog as keystone_service_catalog
from oslo_config import cfg
from oslo_log import log as logging
from oslo_serialization import jsonutils as json
from six.moves.urllib import parse as urlparse

from sahara import context
from sahara import exceptions as ex
from sahara.i18n import _LW

LOG = logging.getLogger(__name__)

# List of the errors, that can be retried
ERRORS_TO_RETRY = [408, 413, 429, 500, 502, 503, 504]

opts = [
    cfg.IntOpt('retries_number',
               default=5,
               help='Number of times to retry the request to client before '
                    'failing'),
    cfg.IntOpt('retry_after',
               default=10,
               help='Time between the retries to client (in seconds).')
]

retries = cfg.OptGroup(name='retries',
                       title='OpenStack clients calls retries')

CONF = cfg.CONF
CONF.register_group(retries)
CONF.register_opts(opts, group=retries)


def url_for(service_catalog=None, service_type='identity',
            endpoint_type="internalURL"):
    if not service_catalog:
        service_catalog = context.current().service_catalog
    try:
        return keystone_service_catalog.ServiceCatalogV2(
            {'serviceCatalog': json.loads(service_catalog)}).url_for(
                service_type=service_type, endpoint_type=endpoint_type,
                region_name=CONF.os_region_name)
    except keystone_ex.EndpointNotFound:
        return keystone_service_catalog.ServiceCatalogV3(
            context.get_auth_token(),
            {'catalog': json.loads(service_catalog)}).url_for(
                service_type=service_type, endpoint_type=endpoint_type,
                region_name=CONF.os_region_name)


def retrieve_auth_url(endpoint_type="internalURL"):
    version = 'v3' if CONF.use_identity_api_v3 else 'v2.0'
    ctx = context.current()
    if ctx.service_catalog:
        info = urlparse.urlparse(url_for(ctx.service_catalog, 'identity',
                                         endpoint_type))
    else:
        info = urlparse.urlparse(CONF.keystone_authtoken.auth_uri)
    return "%s://%s/%s" % (info[:2] + (version,))


def execute_with_retries(method, *args, **kwargs):
    attempts = CONF.retries.retries_number + 1
    while attempts > 0:
        try:
            return method(*args, **kwargs)
        except Exception as e:
            error_code = getattr(e, 'http_status', None) or getattr(
                e, 'status_code', None) or getattr(e, 'code', None)
            if error_code in ERRORS_TO_RETRY:
                LOG.warning(_LW('Occasional error occurred during "{method}" '
                                'execution: {error_msg} ({error_code}). '
                                'Operation will be retried.').format(
                            method=method.__name__,
                            error_msg=e,
                            error_code=error_code))
                attempts -= 1
                retry_after = getattr(e, 'retry_after', 0)
                context.sleep(max(retry_after, CONF.retries.retry_after))
            else:
                LOG.debug('Permanent error occurred during "{method}" '
                          'execution: {error_msg}.'.format(
                              method=method.__name__, error_msg=e))
                raise e
    else:
        raise ex.MaxRetriesExceeded(attempts, method.__name__)
