# 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.

import contextlib
import json
import logging

import keystoneauth1.exceptions.http as ks_exceptions
import osc_lib.exceptions as exceptions

from osc_placement import version


_http_error_to_exc = {
    cls.http_status: cls
    for cls in exceptions.ClientException.__subclasses__()
}


LOG = logging.getLogger(__name__)


@contextlib.contextmanager
def _wrap_http_exceptions():
    """Reraise osc-lib exceptions with detailed messages."""

    try:
        yield
    except ks_exceptions.HttpError as exc:
        if 400 <= exc.http_status < 500:
            detail = json.loads(exc.response.content)['errors'][0]['detail']
            msg = detail.split('\n')[-1].strip()
            exc_class = _http_error_to_exc.get(exc.http_status,
                                               exceptions.CommandError)
            raise exc_class(exc.http_status, msg) from exc
        else:
            raise


class SessionClient(object):
    def __init__(self, session, ks_filter, api_version='1.0'):
        self.session = session
        self.ks_filter = ks_filter
        self.negotiate_api_version(api_version)

    def request(self, method, url, **kwargs):
        version = kwargs.pop('version', None)
        api_version = (self.ks_filter['service_type'] + ' '
                       + (version or self.api_version))
        headers = kwargs.pop('headers', {})
        headers.setdefault('OpenStack-API-Version', api_version)
        headers.setdefault('Accept', 'application/json')

        with _wrap_http_exceptions():
            return self.session.request(url, method,
                                        headers=headers,
                                        endpoint_filter=self.ks_filter,
                                        **kwargs)

    def negotiate_api_version(self, api_version):
        """Set api_version to self.

        If negotiate version (only majorversion) is given, talk to server to
        pick up max microversion supported both by client and by server.
        """
        if api_version not in version.NEGOTIATE_VERSIONS:
            self.api_version = api_version
            return
        client_ver = version.MAX_VERSION_NO_GAP
        self.api_version = client_ver
        resp = self.request('GET', '/', raise_exc=False)
        if resp.status_code == 406:
            server_ver = resp.json()['errors'][0]['max_version']
            self.api_version = server_ver
            LOG.debug('Microversion %s not supported in server. '
                      'Falling back to microversion %s',
                      client_ver, server_ver)
