# Copyright 2015, Ericsson AB
#
# 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.

"""Gluon base exception handling.

Includes decorator for re-raising Cloudpulse-type exceptions.

"""

import six

from oslo_config import cfg

from oslo_log import log as logging

from oslo_log._i18n import _

LOG = logging.getLogger(__name__)
CONF = cfg.CONF


class GluonException(Exception):
    """Base Gluon Exception

    To correctly use this class, inherit from it and define
    a 'message' property. That message will get printf'd
    with the keyword arguments provided to the constructor.

    """
    message = _("An unknown exception occurred.")
    code = 500

    def __init__(self, message=None, **kwargs):
        self.kwargs = kwargs

        if 'code' not in self.kwargs:
            try:
                self.kwargs['code'] = self.code
            except AttributeError:
                pass

        if message:
            self.message = message

        try:
            self.message = self.message % kwargs
        except Exception as e:
            # kwargs doesn't match a variable in the message
            # log the issue and the kwargs
            LOG.exception('Exception in string format operation')
            for name, value in kwargs.items():
                LOG.error("%(name)s: %(value)s" %
                          {'name': name, 'value': value})
            try:
                if CONF.fatal_exception_format_errors:
                    raise e
            except cfg.NoSuchOptError:
                # Note: work around for Bug: #1447873
                if CONF.oslo_versionedobjects.fatal_exception_format_errors:
                    raise e

        super(GluonException, self).__init__(self.message)

    def __str__(self):
        if six.PY3:
            return self.message
        return self.message.encode('utf-8')

    def __unicode__(self):
        return self.message

    def format_message(self):
        if self.__class__.__name__.endswith('_Remote'):
            return self.args[0]
        else:
            return six.text_type(self)


class Conflict(GluonException):
    message = _('Conflict.')
    code = 409


class AlreadyExists(Conflict):
    message = _("Object of %(cls)s with %(key)s \"%(value)s\" already exists.")


class NotFound(GluonException):
    code = 404
    message = _("Object of %(cls)s with Primay Key %(key)s not found.")


class BackendDoesNotExsist(GluonException):
    code = 409
    message = _("Backend with name %(name)s does not exsist.")


class GluonClientException(GluonException):
    """Base exception which exceptions from Gluon are mapped into.

    NOTE: on the client side, we use different exception types in order
    to allow client library users to handle server exceptions in try...except
    blocks. The actual error message is the one generated on the server side.
    """

    status_code = 0

    def __init__(self, message=None, **kwargs):
        if 'status_code' in kwargs:
            self.status_code = kwargs['status_code']
        super(GluonClientException, self).__init__(message, **kwargs)


class EndpointNotFound(GluonClientException):
    message = _("Could not find Service or Region in Service Catalog.")


class EndpointTypeNotFound(GluonClientException):
    message = _("Could not find endpoint type %(type_)s in Service Catalog.")


class AmbiguousEndpoints(GluonClientException):
    message = _("Found more than one matching endpoint in Service Catalog: "
                "%(matching_endpoints)")


class RequestURITooLong(GluonClientException):
    """Raised when a request fails with HTTP error 414."""

    def __init__(self, **kwargs):
        self.excess = kwargs.get('excess', 0)
        super(RequestURITooLong, self).__init__(**kwargs)


class ConnectionFailed(GluonClientException):
    message = _("Connection to Gluon failed: %(reason)s")


class SslCertificateValidationError(GluonClientException):
    message = _("SSL certificate validation has failed: %(reason)s")


class MalformedResponseBody(GluonClientException):
    message = _("Malformed response body: %(reason)s")


class InvalidContentType(GluonClientException):
    message = _("Invalid content type %(content_type)s.")


class InvalidConfigurationOption(GluonClientException):
    """An error due to an invalid configuration option value."""

    message = _("An invalid value for configuration option %(opt_name): "
                "%(opt_value)")


class PolicyInitError(GluonClientException):
    """An error due to policy initialization failure."""

    message = _("Failed to initialize policy %(policy)s because %(reason)s.")


class PolicyCheckError(GluonClientException):
    """An error due to a policy check failure."""

    message = _("Failed to check policy %(policy)s because %(reason)s.")


class InvalidFileFormat(GluonClientException):
    """An error due to a invalid API specification file."""
    message = _("Invalid file format")
