#  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 pecan
from six.moves.urllib import parse

from barbican.api import controllers
from barbican.api.controllers import cas
from barbican.api.controllers import containers
from barbican.api.controllers import orders
from barbican.api.controllers import quotas
from barbican.api.controllers import secrets
from barbican.api.controllers import secretstores
from barbican.api.controllers import transportkeys
from barbican.common import config
from barbican.common import utils
from barbican import i18n as u
from barbican import version

LOG = utils.getLogger(__name__)


CONF = config.CONF


MIME_TYPE_JSON = 'application/json'
MIME_TYPE_JSON_HOME = 'application/json-home'
MEDIA_TYPE_JSON = 'application/vnd.openstack.key-manager-%s+json'


def _version_not_found():
    """Throw exception indicating version not found."""
    pecan.abort(404, u._("The version you requested wasn't found"))


def _get_versioned_url(version):
    if version[-1] != '/':
        version += '/'
    # If host_href is not set in barbican conf, then derive it from request url
    host_part = utils.get_base_url_from_request()
    return parse.urljoin(host_part, version)


class BaseVersionController(object):
    """Base class for the version-specific controllers"""

    @classmethod
    def get_version_info(cls, request):
        return {
            'id': cls.version_id,
            'status': 'stable',
            'updated': cls.last_updated,
            'links': [
                {
                    'rel': 'self',
                    'href': _get_versioned_url(cls.version_string),
                }, {
                    'rel': 'describedby',
                    'type': 'text/html',
                    'href': 'http://docs.openstack.org/'
                }
            ],
            'media-types': [
                {
                    'base': MIME_TYPE_JSON,
                    'type': MEDIA_TYPE_JSON % cls.version_string
                }
            ]
        }


class V1Controller(BaseVersionController):
    """Root controller for the v1 API"""

    version_string = 'v1'

    # NOTE(jaosorior): We might start using decimals in the future, meanwhile
    # this is the same as the version string.
    version_id = 'v1'

    last_updated = '2015-04-28T00:00:00Z'

    def __init__(self):
        LOG.debug('=== Creating V1Controller ===')
        self.secrets = secrets.SecretsController()
        self.orders = orders.OrdersController()
        self.containers = containers.ContainersController()
        self.transport_keys = transportkeys.TransportKeysController()
        self.cas = cas.CertificateAuthoritiesController()
        self.quotas = quotas.QuotasController()
        setattr(self, 'project-quotas', quotas.ProjectsQuotasController())
        setattr(self, 'secret-stores', secretstores.SecretStoresController())

    @pecan.expose(generic=True)
    def index(self):
        pecan.abort(405)  # HTTP 405 Method Not Allowed as default

    @index.when(method='GET', template='json')
    @utils.allow_certain_content_types(MIME_TYPE_JSON, MIME_TYPE_JSON_HOME)
    @controllers.handle_exceptions(u._('Version retrieval'))
    def on_get(self):
        pecan.core.override_template('json')
        return {'version': self.get_version_info(pecan.request)}


AVAILABLE_VERSIONS = {
    V1Controller.version_string: V1Controller,
}

DEFAULT_VERSION = V1Controller.version_string


class VersionsController(object):

    def __init__(self):
        LOG.debug('=== Creating VersionsController ===')

    @pecan.expose(generic=True)
    def index(self, **kwargs):
        pecan.abort(405)  # HTTP 405 Method Not Allowed as default

    @index.when(method='GET', template='json')
    @utils.allow_certain_content_types(MIME_TYPE_JSON, MIME_TYPE_JSON_HOME)
    def on_get(self, **kwargs):
        """The list of versions is dependent on the context."""
        self._redirect_to_default_json_home_if_needed(pecan.request)

        if 'build' in kwargs:
            return {'build': version.__version__}

        versions_info = [version_class.get_version_info(pecan.request) for
                         version_class in AVAILABLE_VERSIONS.values()]

        version_output = {
            'versions': {
                'values': versions_info
            }
        }

        # Since we are returning all the versions available, the proper status
        # code is Multiple Choices (300)
        pecan.response.status = 300
        return version_output

    def _redirect_to_default_json_home_if_needed(self, request):
        if self._mime_best_match(request.accept) == MIME_TYPE_JSON_HOME:
            url = _get_versioned_url(DEFAULT_VERSION)
            LOG.debug("Redirecting Request to " + url)
            # NOTE(jaosorior): This issues an "external" redirect because of
            # two reasons:
            # * This module doesn't require authorization, and accessing
            #   specific version info needs that.
            # * The resource is a separate app_factory and won't be found
            #   internally
            pecan.redirect(url, request=request)

    def _mime_best_match(self, accept):
        if not accept:
            return MIME_TYPE_JSON

        SUPPORTED_TYPES = [MIME_TYPE_JSON, MIME_TYPE_JSON_HOME]
        return accept.best_match(SUPPORTED_TYPES)
