# Copyright (c) 2016 NTT DATA
# 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.

"""The Host API extension."""

from oslo_utils import encodeutils
from oslo_utils import strutils
from six.moves import http_client as http
from webob import exc

from masakari.api.openstack import common
from masakari.api.openstack import extensions
from masakari.api.openstack.ha.schemas import hosts as schema
from masakari.api.openstack import wsgi
from masakari.api import validation
from masakari import exception
from masakari.ha import api as host_api
from masakari.i18n import _
from masakari import objects
from masakari.policies import hosts as host_policies

ALIAS = "os-hosts"


class HostsController(wsgi.Controller):
    """The Host API controller for the OpenStack API."""

    def __init__(self):
        self.api = host_api.HostAPI()

    @extensions.expected_errors((http.BAD_REQUEST, http.FORBIDDEN,
                                 http.NOT_FOUND))
    def index(self, req, segment_id):
        """Returns a list a hosts."""
        context = req.environ['masakari.context']
        context.can(host_policies.HOSTS % 'index')

        try:
            filters = {}
            limit, marker = common.get_limit_and_marker(req)
            sort_keys, sort_dirs = common.get_sort_params(req.params)

            segment = objects.FailoverSegment.get_by_uuid(context,
                                                          segment_id)

            filters['failover_segment_id'] = segment.uuid
            if 'name' in req.params:
                filters['name'] = req.params['name']

            if 'type' in req.params:
                filters['type'] = req.params['type']

            if 'control_attributes' in req.params:
                filters['control_attributes'] = req.params[
                    'control_attributes']

            if 'on_maintenance' in req.params:
                try:
                    filters['on_maintenance'] = strutils.bool_from_string(
                        req.params['on_maintenance'], strict=True)
                except ValueError as ex:
                    msg = _("Invalid value for on_maintenance: "
                            "%s") % encodeutils.exception_to_unicode(ex)
                    raise exc.HTTPBadRequest(explanation=msg)

            if 'reserved' in req.params:
                try:
                    filters['reserved'] = strutils.bool_from_string(
                        req.params['reserved'], strict=True)
                except ValueError as ex:
                    msg = _("Invalid value for reserved: "
                            "%s") % encodeutils.exception_to_unicode(ex)
                    raise exc.HTTPBadRequest(explanation=msg)

            hosts = self.api.get_all(context, filters=filters,
                                     sort_keys=sort_keys, sort_dirs=sort_dirs,
                                     limit=limit, marker=marker)
        except exception.MarkerNotFound as ex:
            raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.Invalid as e:
            raise exc.HTTPBadRequest(explanation=e.format_message())
        except exception.FailoverSegmentNotFound as ex:
            raise exc.HTTPNotFound(explanation=ex.format_message())

        return {'hosts': hosts}

    @wsgi.response(http.CREATED)
    @extensions.expected_errors((http.BAD_REQUEST, http.FORBIDDEN,
                                 http.NOT_FOUND, http.CONFLICT))
    @validation.schema(schema.create)
    def create(self, req, segment_id, body):
        """Creates a host."""
        context = req.environ['masakari.context']
        context.can(host_policies.HOSTS % 'create')
        host_data = body.get('host')
        try:
            host = self.api.create_host(context, segment_id, host_data)
        except exception.HypervisorNotFoundByName as e:
            raise exc.HTTPBadRequest(explanation=e.message)
        except exception.FailoverSegmentNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.HostExists as e:
            raise exc.HTTPConflict(explanation=e.format_message())

        return {'host': host}

    @extensions.expected_errors((http.FORBIDDEN, http.NOT_FOUND))
    def show(self, req, segment_id, id):
        """Shows the details of a host."""
        context = req.environ['masakari.context']
        context.can(host_policies.HOSTS % 'detail')
        try:
            host = self.api.get_host(context, segment_id, id)
        except exception.HostNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.FailoverSegmentNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        return {'host': host}

    @extensions.expected_errors((http.BAD_REQUEST, http.FORBIDDEN,
                                 http.NOT_FOUND, http.CONFLICT))
    @validation.schema(schema.update)
    def update(self, req, segment_id, id, body):
        """Updates the existing host."""
        context = req.environ['masakari.context']
        context.can(host_policies.HOSTS % 'update')
        host_data = body.get('host')
        try:
            host = self.api.update_host(context, segment_id, id, host_data)
        except exception.HypervisorNotFoundByName as e:
            raise exc.HTTPBadRequest(explanation=e.message)
        except exception.HostNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.FailoverSegmentNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except (exception.HostExists, exception.Conflict) as e:
            raise exc.HTTPConflict(explanation=e.format_message())

        return {'host': host}

    @wsgi.response(http.NO_CONTENT)
    @extensions.expected_errors((http.FORBIDDEN, http.NOT_FOUND,
                                 http.CONFLICT))
    def delete(self, req, segment_id, id):
        """Removes a host by id."""
        context = req.environ['masakari.context']
        context.can(host_policies.HOSTS % 'delete')
        try:
            self.api.delete_host(context, segment_id, id)
        except exception.FailoverSegmentNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.HostNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.Conflict as e:
            raise exc.HTTPConflict(explanation=e.format_message())


class Hosts(extensions.V1APIExtensionBase):
    """Hosts controller"""

    name = "Hosts"
    alias = ALIAS
    version = 1

    def get_resources(self):
        parent = {'member_name': 'segment',
                  'collection_name': 'segments'}
        resources = [
            extensions.ResourceExtension(
                'hosts', HostsController(), parent=parent,
                member_name='host')]

        return resources

    def get_controller_extensions(self):
        return []
