# Copyright 2012 IBM Corp.
# Copyright (c) 2015 Mirantis inc.
# All Rights Reserved.
#
#    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 http.client as http_client
from oslo_utils import strutils
import webob.exc

from manila.api.openstack import wsgi
from manila.api.views import services as services_views
from manila import db
from manila import exception
from manila.i18n import _
from manila.services import api as service_api


class ServiceMixin(object):
    """The Services API controller common logic.

    Mixin class that should be inherited by Services API controllers,
    which are used for different API URLs and microversions.
    """

    resource_name = "service"
    _view_builder_class = services_views.ViewBuilder

    @wsgi.Controller.authorize("index")
    def _index(self, req, support_ensure_shares=False):
        """Return a list of all running services."""

        context = req.environ['manila.context']
        all_services = db.service_get_all(context)

        services = []
        for service in all_services:
            service_data = {
                'id': service['id'],
                'binary': service['binary'],
                'host': service['host'],
                'zone': service['availability_zone']['name'],
                'status': 'disabled' if service['disabled'] else 'enabled',
                'disabled_reason': service.get('disabled_reason'),
                'state': service['state'],
                'updated_at': service['updated_at'],
            }
            if support_ensure_shares:
                service_data['ensuring'] = service['ensuring']
            services.append(service_data)

        search_opts = [
            'host',
            'binary',
            'zone',
            'state',
            'status',
        ]
        for search_opt in search_opts:
            if search_opt in req.GET:
                value = req.GET[search_opt]
                services = [s for s in services if s[search_opt] == value]
            if len(services) == 0:
                break

        return self._view_builder.detail_list(req, services)

    @wsgi.Controller.authorize("update")
    def _update(self, req, id, body, support_disabled_reason=True):
        """Enable/Disable scheduling for a service."""
        context = req.environ['manila.context']
        update_dict = {}

        if id == "enable":
            data = {'disabled': False}
            if support_disabled_reason:
                update_dict['disabled_reason'] = None
        elif id == "disable":
            data = {'disabled': True}
            disabled_reason = body.get('disabled_reason')
            if disabled_reason and not support_disabled_reason:
                msg = _("'disabled_reason' option is not supported by this "
                        "microversion. Use 2.83 or greater microversion to "
                        "be able to set 'disabled_reason'.")
                raise webob.exc.HTTPBadRequest(explanation=msg)
            if disabled_reason:
                try:
                    strutils.check_string_length(disabled_reason.strip(),
                                                 name='disabled_reason',
                                                 min_length=1,
                                                 max_length=255)
                except (ValueError, TypeError):
                    msg = _('Disabled reason contains invalid characters '
                            'or is too long')
                    raise webob.exc.HTTPBadRequest(explanation=msg)
                update_dict['disabled_reason'] = disabled_reason.strip()
                data['disabled_reason'] = disabled_reason.strip()
        else:
            raise webob.exc.HTTPNotFound("Unknown action '%s'" % id)

        try:
            data['host'] = body['host']
            data['binary'] = body['binary']
        except (TypeError, KeyError):
            raise webob.exc.HTTPBadRequest()

        svc = db.service_get_by_args(context, data['host'], data['binary'])
        update_dict['disabled'] = data['disabled']

        db.service_update(context, svc['id'], update_dict)
        data['status'] = 'disabled' if id == "disable" else 'enabled'

        return self._view_builder.summary(req, data)


class ServiceControllerLegacy(ServiceMixin, wsgi.Controller):
    """Deprecated Services API controller.

    Used by legacy API v1 and v2 microversions from 2.0 to 2.6.
    Registered under deprecated API URL 'os-services'.
    """

    @wsgi.Controller.api_version('1.0', '2.6')
    def index(self, req):
        return self._index(req)

    @wsgi.Controller.api_version('1.0', '2.6')
    def update(self, req, id, body):
        return self._update(req, id, body, support_disabled_reason=False)


class ServiceController(ServiceMixin, wsgi.Controller):
    """Services API controller.

    Used only by API v2 starting from microversion 2.7.
    Registered under API URL 'services'.
    """

    def __init__(self):
        super().__init__()
        self.service_api = service_api.API()

    @wsgi.Controller.api_version('2.7', '2.85')
    def index(self, req):
        return self._index(req)

    @wsgi.Controller.api_version('2.86') # noqa
    def index(self, req): # pylint: disable=function-redefined  # noqa F811
        return self._index(req, support_ensure_shares=True)

    @wsgi.Controller.api_version('2.7', '2.82')
    def update(self, req, id, body):
        return self._update(req, id, body, support_disabled_reason=False)

    @wsgi.Controller.api_version('2.83') # noqa
    def update(self, req, id, body): # pylint: disable=function-redefined  # noqa F811
        return self._update(req, id, body)

    @wsgi.Controller.api_version('2.86')
    @wsgi.Controller.authorize
    def ensure_shares(self, req, body):
        """Starts ensure shares for a given manila-share binary."""
        context = req.environ['manila.context']

        host = body.get('host', None)
        if not host:
            raise webob.exc.HTTPBadRequest('Missing host parameter.')

        try:
            # The only binary supported is Manila share.
            service = db.service_get_by_args(context, host, 'manila-share')
        except exception.NotFound:
            raise webob.exc.HTTPNotFound(
                "manila-share binary for '%s' host not found" % id
            )

        try:
            self.service_api.ensure_shares(context, service, host)
        except webob.exc.HTTPConflict:
            raise

        return webob.Response(status_int=http_client.ACCEPTED)


def create_resource_legacy():
    return wsgi.Resource(ServiceControllerLegacy())


def create_resource():
    return wsgi.Resource(ServiceController())
