# Copyright 2012,  Nachi Ueno,  NTT MCL,  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.

"""
Views for managing Neutron Routers.
"""
import logging

from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _

from horizon import exceptions
from horizon import forms
from horizon import messages

from openstack_dashboard import api

LOG = logging.getLogger(__name__)


class CreateForm(forms.SelfHandlingForm):
    name = forms.CharField(max_length=255, label=_("Router Name"),
                           required=False)
    admin_state_up = forms.BooleanField(
        label=_("Enable Admin State"),
        initial=True,
        required=False,
        help_text=_("If checked, the router will be enabled."))
    external_network = forms.ThemableChoiceField(label=_("External Network"),
                                                 required=False)
    enable_snat = forms.BooleanField(label=_("Enable SNAT"),
                                     initial=True,
                                     required=False)
    mode = forms.ThemableChoiceField(label=_("Router Type"))
    ha = forms.ThemableChoiceField(label=_("High Availability Mode"))
    az_hints = forms.MultipleChoiceField(
        label=_("Availability Zone Hints"),
        required=False,
        help_text=_("Availability Zones where the router may be scheduled. "
                    "Leaving this unset is equivalent to selecting all "
                    "Availability Zones"))
    failure_url = 'horizon:project:routers:index'

    def __init__(self, request, *args, **kwargs):
        super().__init__(request, *args, **kwargs)
        self.dvr_allowed = api.neutron.get_feature_permission(self.request,
                                                              "dvr", "create")
        if self.dvr_allowed:
            mode_choices = [('server_default', _('Use Server Default')),
                            ('centralized', _('Centralized')),
                            ('distributed', _('Distributed'))]
            self.fields['mode'].choices = mode_choices
        else:
            del self.fields['mode']

        self.ha_allowed = api.neutron.get_feature_permission(self.request,
                                                             "l3-ha", "create")
        if self.ha_allowed:
            ha_choices = [('server_default', _('Use Server Default')),
                          ('enabled', _('Enable HA mode')),
                          ('disabled', _('Disable HA mode'))]
            self.fields['ha'].choices = ha_choices
        else:
            del self.fields['ha']
        networks = self._get_network_list(request)
        if networks:
            self.fields['external_network'].choices = networks
        else:
            del self.fields['external_network']

        self.enable_snat_allowed = self.initial['enable_snat_allowed']
        if (not networks or not self.enable_snat_allowed):
            del self.fields['enable_snat']

        try:
            az_supported = api.neutron.is_extension_supported(
                self.request, 'router_availability_zone')

            if az_supported:
                zones = api.neutron.list_availability_zones(
                    self.request, 'router', 'available')
                self.fields['az_hints'].choices = [(zone['name'], zone['name'])
                                                   for zone in zones]
            else:
                del self.fields['az_hints']
        except Exception:
            msg = _("Failed to get availability zone list.")
            exceptions.handle(self.request, msg)
            del self.fields['az_hints']

    def _get_network_list(self, request):
        search_opts = {'router:external': True}
        try:
            networks = api.neutron.network_list(request, **search_opts)
        except Exception as e:
            LOG.info('Failed to get network list: %s', e)
            msg = _('Failed to get network list.')
            messages.warning(request, msg)
            networks = []

        choices = [(network.id, network.name or network.id)
                   for network in networks]
        if choices:
            choices.insert(0, ("", _("Select network")))
        return choices

    def handle(self, request, data):
        try:
            params = {'name': data['name'],
                      'admin_state_up': data['admin_state_up']}
            # NOTE: admin form allows to specify tenant_id.
            # We have the logic here to simplify the logic.
            if 'tenant_id' in data and data['tenant_id']:
                params['tenant_id'] = data['tenant_id']
            if 'external_network' in data and data['external_network']:
                params['external_gateway_info'] = {'network_id':
                                                   data['external_network']}
                if self.enable_snat_allowed:
                    params['external_gateway_info']['enable_snat'] = \
                        data['enable_snat']
            if 'az_hints' in data and data['az_hints']:
                params['availability_zone_hints'] = data['az_hints']
            if (self.dvr_allowed and data['mode'] != 'server_default'):
                params['distributed'] = (data['mode'] == 'distributed')
            if (self.ha_allowed and data['ha'] != 'server_default'):
                params['ha'] = (data['ha'] == 'enabled')
            router = api.neutron.router_create(request, **params)
            message = (_('Router %s was successfully created.') %
                       router.name_or_id)
            messages.success(request, message)
            return router
        except Exception as exc:
            LOG.info('Failed to create router: %s', exc)
            if exc.status_code == 409:
                msg = _('Quota exceeded for resource router.')
            else:
                if data["name"]:
                    msg = _('Failed to create router "%s".') % data['name']
                else:
                    msg = _('Failed to create router.')
            redirect = reverse(self.failure_url)
            exceptions.handle(request, msg, redirect=redirect)
            return False


class UpdateForm(forms.SelfHandlingForm):
    name = forms.CharField(label=_("Name"), required=False)
    admin_state = forms.BooleanField(
        label=_("Enable Admin State"),
        required=False,
        help_text=_("If checked, the router will be enabled."))
    mode = forms.ThemableChoiceField(label=_("Router Type"))
    ha = forms.BooleanField(label=_("High Availability Mode"), required=False)

    redirect_url = reverse_lazy('horizon:project:routers:index')

    def __init__(self, request, *args, **kwargs):
        super().__init__(request, *args, **kwargs)
        self.dvr_allowed = api.neutron.get_feature_permission(self.request,
                                                              "dvr", "update")
        if not self.dvr_allowed:
            del self.fields['mode']
        elif self.initial.get('mode') == 'distributed':
            # Neutron supports only changing from centralized to
            # distributed now.
            mode_choices = [('distributed', _('Distributed'))]
            self.fields['mode'].widget = forms.TextInput(attrs={'readonly':
                                                                'readonly'})
            self.fields['mode'].choices = mode_choices
        else:
            mode_choices = [('centralized', _('Centralized')),
                            ('distributed', _('Distributed'))]
            self.fields['mode'].choices = mode_choices

        # TODO(amotoki): Due to Neutron Bug 1378525, Neutron disables
        # PUT operation. It will be fixed in Kilo cycle.
        # self.ha_allowed = api.neutron.get_feature_permission(
        #     self.request, "l3-ha", "update")
        self.ha_allowed = False
        if not self.ha_allowed:
            del self.fields['ha']

    def handle(self, request, data):
        try:
            params = {'admin_state_up': data['admin_state'],
                      'name': data['name']}
            if self.dvr_allowed:
                params['distributed'] = (data['mode'] == 'distributed')
            if self.ha_allowed:
                params['ha'] = data['ha']
            router = api.neutron.router_update(request,
                                               self.initial['router_id'],
                                               **params)
            msg = _('Router %s was successfully updated.') % router.name_or_id
            messages.success(request, msg)
            return router
        except Exception as exc:
            LOG.info('Failed to update router %(id)s: %(exc)s',
                     {'id': self.initial['router_id'], 'exc': exc})
            name_or_id = data['name'] or self.initial['router_id']
            msg = _('Failed to update router %s') % name_or_id
            exceptions.handle(request, msg, redirect=self.redirect_url)
