#
#    (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
#
# 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.
"""API over the neutron service."""

from django.utils.translation import gettext_lazy as _
from django.views import generic

from openstack_dashboard import api
from openstack_dashboard.api.rest import urls
from openstack_dashboard.api.rest import utils as rest_utils
from openstack_dashboard.usage import quotas


@urls.register
class Networks(generic.View):
    """API for Neutron Networks

    https://docs.openstack.org/api-ref/network/v2/index.html
    """
    url_regex = r'neutron/networks/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of networks for a project

        The listing result is an object with property "items".  Each item is
        a network.
        """
        tenant_id = request.user.tenant_id
        # NOTE(amotoki): At now, this method is only for server create,
        # so it is no problem to pass include_pre_auto_allocate=True always.
        # We need to revisit the logic if we use this method for
        # other operations other than server create.
        result = api.neutron.network_list_for_tenant(
            request, tenant_id,
            include_pre_auto_allocate=True)
        return {'items': [n.to_dict() for n in result]}

    @rest_utils.ajax(data_required=True)
    def post(self, request):
        """Create a network

        :param  admin_state_up (optional): The administrative state of the
                network, which is up (true) or down (false).
        :param name (optional): The network name. A request body is optional:
                If you include it, it can specify this optional attribute.
        :param shared (optional): Indicates whether this network is shared
                across all tenants. By default, only administrative users can
                change this value.
        :param tenant_id (optional): Admin-only. The UUID of the tenant that
                will own the network. This tenant can be different from the
                tenant that makes the create network request. However, only
                administrative users can specify a tenant ID other than their
                own. You cannot change this value through authorization
                policies.

         :return: JSON representation of a Network
         """
        new_network = api.neutron.network_create(request, **request.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/networks/%s' % new_network.id,
            new_network.to_dict()
        )


@urls.register
class Subnets(generic.View):
    """API for Neutron Subnets

    https://docs.openstack.org/api-ref/network/v2/index.html#subnets
    """
    url_regex = r'neutron/subnets/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of subnets for a project

        The listing result is an object with property "items".  Each item is
        a subnet.

        """
        result = api.neutron.subnet_list(request, **request.GET.dict())
        return {'items': [n.to_dict() for n in result]}

    @rest_utils.ajax(data_required=True)
    def post(self, request):
        """Create a Subnet for a given Network

        :param name (optional):  The subnet name.
        :param network_id: The ID of the attached network.
        :param tenant_id (optional): The ID of the tenant who owns the network.
                Only administrative users can specify a tenant ID other than
                their own.
        :param allocation_pools (optional): The start and end addresses for the
                allocation pools.
        :param gateway_ip (optional): The gateway IP address.
        :param ip_version: The IP version, which is 4 or 6.
        :param cidr: The CIDR.
        :param id (optional): The ID of the subnet.
        :param enable_dhcp (optional): Set to true if DHCP is enabled and false
                if DHCP is disabled.

        :return: JSON representation of a Subnet

        """
        new_subnet = api.neutron.subnet_create(request, **request.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/subnets/%s' % new_subnet.id,
            new_subnet.to_dict()
        )


@urls.register
class Ports(generic.View):
    """API for Neutron Ports

    https://docs.openstack.org/api-ref/network/v2/index.html#ports
    """
    url_regex = r'neutron/ports/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of ports for a network

        The listing result is an object with property "items".  Each item is
        a port.
        """
        # https://opendev.org/openstack/neutron/src/branch/master/neutron/tests/unit/extensions/v2attributes.py
        params = request.GET.dict()
        params.setdefault("project_id", request.user.project_id)
        result = api.neutron.port_list_with_trunk_types(request, **params)
        return {'items': [n.to_dict() for n in result]}


@urls.register
class Trunk(generic.View):
    """API for a single neutron Trunk"""
    url_regex = r'neutron/trunks/(?P<trunk_id>[^/]+)/$'

    @rest_utils.ajax()
    def delete(self, request, trunk_id):
        api.neutron.trunk_delete(request, trunk_id)

    @rest_utils.ajax()
    def get(self, request, trunk_id):
        """Get a specific trunk"""
        trunk = api.neutron.trunk_show(request, trunk_id)
        return trunk.to_dict()

    @rest_utils.ajax(data_required=True)
    def patch(self, request, trunk_id):
        """Update a specific trunk"""
        old_trunk = request.DATA[0]
        new_trunk = request.DATA[1]

        return api.neutron.trunk_update(
            request, trunk_id, old_trunk, new_trunk)


@urls.register
class Trunks(generic.View):
    """API for neutron Trunks"""
    url_regex = r'neutron/trunks/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of trunks

        The listing result is an object with property "items".
        Each item is a trunk.
        """
        result = api.neutron.trunk_list(request, **request.GET.dict())
        return {'items': [n.to_dict() for n in result]}

    @rest_utils.ajax(data_required=True)
    def post(self, request):
        new_trunk = api.neutron.trunk_create(request, **request.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/trunks/%s' % new_trunk.id,
            new_trunk.to_dict()
        )


@urls.register
class Services(generic.View):
    """API for Neutron agents"""
    url_regex = r'neutron/agents/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of agents"""
        if (api.base.is_service_enabled(request, 'network') and
                api.neutron.is_extension_supported(request, 'agent')):
            result = api.neutron.agent_list(request, **request.GET.dict())
            return {'items': [n.to_dict() for n in result]}
        raise rest_utils.AjaxError(501, '')


@urls.register
class Extensions(generic.View):
    """API for neutron extensions."""
    url_regex = r'neutron/extensions/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of extensions.

        The listing result is an object with property "items". Each item is
        an extension.

        Example:
        http://localhost/api/neutron/extensions
        """
        result = api.neutron.list_extensions(request)
        # list_extensions can return a tuple, so list() is required.
        return {'items': list(result)}


class DefaultQuotaSets(generic.View):
    """API for getting default quotas for neutron"""
    url_regex = r'neutron/quota-sets/defaults/$'

    @rest_utils.ajax()
    def get(self, request):
        if not api.base.is_service_enabled(request, 'network'):
            raise rest_utils.AjaxError(501, _('Service Neutron is disabled.'))

        quota_set = api.neutron.tenant_quota_get(
            request, request.user.tenant_id)

        result = [{
            'display_name': quotas.QUOTA_NAMES.get(
                quota.name,
                quota.name.replace('_', ' ').title()
            ) + '',
            'name': quota.name,
            'limit': quota.limit
        } for quota in quota_set]

        return {'items': result}


@urls.register
class QuotasSets(generic.View):
    """API for setting quotas of a given project."""
    url_regex = r'neutron/quotas-sets/(?P<project_id>[0-9a-f]+)$'

    @rest_utils.ajax(data_required=True)
    def patch(self, request, project_id):
        """Update a single project quota data.

        The PATCH data should be an application/json object with the
        attributes to set to new quota values.

        This method returns HTTP 204 (no content) on success.
        """
        # Filters only neutron quota fields
        disabled_quotas = quotas.get_disabled_quotas(request)

        if api.base.is_service_enabled(request, 'network') and \
                api.neutron.is_extension_supported(request, 'quotas'):
            neutron_data = {
                key: request.DATA[key] for key in quotas.NEUTRON_QUOTA_FIELDS
                if key not in disabled_quotas
            }

            api.neutron.tenant_quota_update(request,
                                            project_id,
                                            **neutron_data)
        else:
            message = _('Service Neutron is disabled or quotas extension not '
                        'available.')
            raise rest_utils.AjaxError(501, message)


@urls.register
class QoSPolicies(generic.View):
    """API for QoS Policy."""
    url_regex = r'neutron/qos_policies/$'

    @rest_utils.ajax()
    def get(self, request):
        """Get a list of QoS policies.

        The listing result is an object with property "items".
        Each item is a qos policy.
        """
        result = api.neutron.policy_list(request,
                                         tenant_id=request.user.project_id)
        return {'items': [p.to_dict() for p in result]}

    @rest_utils.ajax(data_required=True)
    def post(self, request):
        """Create a Network QoS policy.

        Create a qos policy using parameters supplied in the POST
        application/json object. The "name" (string) parameter is required.
        This method returns the new qos policy object on success.
        """
        qospolicy = api.neutron.policy_create(request, **request.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/qos_policies/%s' % qospolicy.id,
            qospolicy.to_dict()
        )


@urls.register
class QoSPolicy(generic.View):
    """API for a single QoS Policy."""
    url_regex = r'neutron/qos_policies/(?P<policy_id>[^/]+)/$'

    @rest_utils.ajax()
    def delete(self, request, policy_id):
        api.neutron.policy_delete(request, policy_id)

    @rest_utils.ajax()
    def get(self, request, policy_id):
        """Get a specific policy"""
        policy = api.neutron.policy_get(request, policy_id)
        return policy.to_dict()


@urls.register
class MinimumBandwidthRules(generic.View):
    """API for Minimum Bandwidth Rule create."""
    url_regex = (
        r'neutron/qos/policies/' +
        r'(?P<policy_id>[^/]+)/minimum_bandwidth_rules/$')

    @rest_utils.ajax(data_required=True)
    def post(self, req, policy_id):
        minimum_bandwidth_rule = api.neutron.minimum_bandwidth_rule_create(
            req, policy_id, **req.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/qospolicies/minimumbandwidthrules/%s'
            % minimum_bandwidth_rule.id,
            minimum_bandwidth_rule.to_dict()
        )


@urls.register
class MinimumBandwidthRule(generic.View):
    """API for Minimum Bandwidth Rule update and delete."""
    url_regex = (
        r'neutron/qos/policies/' +
        r'(?P<policy_id>[^/]+)/minimum_bandwidth_rules/(?P<rule_id>[^/]+)$'
    )

    @rest_utils.ajax(data_required=True)
    def patch(self, req, policy_id, rule_id):
        """Update a QoS Minimum Bandwidth rule."""
        return api.neutron.minimum_bandwidth_rule_update(
            req, policy_id, rule_id, **req.DATA)

    @rest_utils.ajax()
    def delete(self, req, policy_id, rule_id):
        """Delete a QoS Minimum Badwidth rule"""
        api.neutron.minimum_bandwidth_rule_delete(req, policy_id, rule_id)


@urls.register
class DSCPMarkingRules(generic.View):
    """API for DSCP Marking Rule create"""
    url_regex = (
        r'neutron/qos/policies/(?P<policy_id>[^/]+)' +
        r'/dscp_marking_rules/$')

    @rest_utils.ajax(data_required=True)
    def post(self, req, policy_id):
        """Create QoS DSCP Marking rules."""
        dscp_marking_rule = api.neutron.dscp_marking_rule_create(
            req, policy_id, **req.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/qospolicies/dscpmarkingrules/%s'
            % dscp_marking_rule.id,
            dscp_marking_rule.to_dict()
        )


@urls.register
class DSCPMarkingRule(generic.View):
    """API for DSCP Marking Rule Delete and Update"""
    url_regex = (
        r'neutron/qos/policies/(?P<policy_id>[^/]+)' +
        r'/dscp_marking_rules/(?P<rule_id>[^/]+)$')

    @rest_utils.ajax(data_required=True)
    def patch(self, req, policy_id, rule_id):
        """Update a qos DSCP Marking rule."""
        return api.neutron.dscp_marking_rule_update(
            req, policy_id, rule_id, **req.DATA)

    @rest_utils.ajax()
    def delete(self, req, policy_id, rule_id):
        """Delete a qos DSCP Marking rule."""
        api.neutron.dscp_marking_rule_delete(req, policy_id, rule_id)


@urls.register
class BandwidthLimitRules(generic.View):
    """API for Bandwidth Limit Rule Create"""
    url_regex = (
        r'neutron/qos/policies/(?P<policy_id>[^/]+)' +
        r'/bandwidth_limit_rules/$'
    )

    @rest_utils.ajax(data_required=True)
    def post(self, req, policy_id):
        """Create QoS Bandwidth Limit rules."""
        bandwidth_limit_rule = api.neutron.bandwidth_limit_rule_create(
            req, policy_id, **req.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/qospolicies/bandwidthlimitrules/%s'
            % bandwidth_limit_rule.id,
            bandwidth_limit_rule.to_dict()
        )


@urls.register
class BandwidthLimitRule(generic.View):
    """API for Bandwidth Limit Rule Update and Delete"""
    url_regex = (
        r'neutron/qos/policies/(?P<policy_id>[^/]+)' +
        r'/bandwidth_limit_rules/(?P<rule_id>[^/]+)$')

    @rest_utils.ajax(data_required=True)
    def patch(self, req, policy_id, rule_id):
        """Update a QoS Bandwidth Limit rule."""
        return api.neutron.bandwidth_limit_rule_update(
            req, policy_id, rule_id, **req.DATA)

    @rest_utils.ajax()
    def delete(self, req, policy_id, rule_id):
        """Delete a QoS Bandwidth Limit rule."""
        api.neutron.bandwidth_limit_rule_delete(req, policy_id, rule_id)


@urls.register
class MinimumPacketRateRules(generic.View):
    """API for Minimum Packet Rate Rule Create."""
    url_regex = (
        r'neutron/qos/policies/' +
        r'(?P<policy_id>[^/]+)/minimum_packet_rate_rules/$')

    @rest_utils.ajax(data_required=True)
    def post(self, req, policy_id):
        """Create QoS Minimum Packet Rate rules."""
        minimum_packet_rate_rule = api.neutron.minimum_packet_rate_rule_create(
            req, policy_id, **req.DATA)
        return rest_utils.CreatedResponse(
            '/api/neutron/qospolicies/minimumpacketraterules/%s'
            % minimum_packet_rate_rule.id,
            minimum_packet_rate_rule.to_dict()
        )


@urls.register
class MinimumPacketRateRule(generic.View):
    """API for Updating and Deleting Minimum Packet Rate Rule"""
    url_regex = (
        r'neutron/qos/policies/' +
        r'(?P<policy_id>[^/]+)/minimum_packet_rate_rules/(?P<rule_id>[^/]+)$'
    )

    @rest_utils.ajax(data_required=True)
    def patch(self, req, policy_id, rule_id):
        """Update a QoS Minimum Packet Rate rule."""
        return api.neutron.minimum_packet_rate_rule_update(
            req, policy_id, rule_id, **req.DATA)

    @rest_utils.ajax()
    def delete(self, req, policy_id, rule_id):
        """Delete a QoS Minimum Packet Rate rule."""
        api.neutron.minimum_packet_rate_rule_delete(req, policy_id, rule_id)
