#    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 logging

import re

from django.conf import settings
from django import shortcuts
from django.utils.translation import ugettext_lazy as _
from django.views import generic

from openstack_dashboard import api
from openstack_dashboard.api.rest import utils as rest_utils

from horizon import exceptions

from gbpui import client

from netaddr import IPAddress
from netaddr import IPNetwork

LOG = logging.getLogger(__name__)


class PolicyTargets(generic.View):
    # todo: This is a direct port from the old form version; it needs to be
    #       tested and revised; It may be dodgy and/or redundant because:
    #       1)  The created regex might be nonsenseical (needs testing)
    #       2)  The proxy_group_ids might be logical duplication of the third
    #           third conditional
    @staticmethod
    def is_proxy_group(policy_target, proxy_group_ids):
        if hasattr(settings, 'GBPUI_HIDE_PTG_NAMES_FROM_MEMBER_CREATE'):
            regex = "(" + ")|(".join(
                settings.GBPUI_HIDE_PTG_NAMES_FROM_MEMBER_CREATE) \
                + ")"

            if re.match(regex, policy_target.get('name')):
                return True

        if policy_target.id in proxy_group_ids:
            return True

        if policy_target.get('proxied_group_id'):
            return True

        return False

    @rest_utils.ajax()
    def get(self, request):
        policy_targets = client.policy_target_list(
            request, tenant_id=request.user.tenant_id
        )

        proxy_group_ids = [pt.get('proxy_group_id') for pt in policy_targets
                           if pt.get('proxy_group_id')]

        try:
            policy_target_objects = []

            for policy_target in policy_targets:
                if not self.is_proxy_group(policy_target, proxy_group_ids):
                    subnet_objects = []

                    for subnet_id in policy_target.subnets:
                        try:
                            subnet = api.neutron.subnet_get(request, subnet_id)
                            allocation_pool_objects = []

                            allocation_pools = subnet['allocation_pools']
                            if allocation_pools:
                                for allocation_pool in allocation_pools:
                                    allocation_pool_object = {
                                        "start": allocation_pool['start'],
                                        "end": allocation_pool['end']
                                    }
                                    allocation_pool_objects.append(
                                        allocation_pool_object)

                            subnet_object = {
                                "cidr": subnet['cidr'],
                                "allocation_pools": allocation_pool_objects
                            }
                            subnet_objects.append(subnet_object)
                        except Exception:
                            LOG.exception("Unable to retrieve subnet.")

                    policy_target_object = {
                        "id": policy_target.id,
                        "name_or_id": policy_target.name_or_id,
                        "subnets": subnet_objects
                    }
                    policy_target_objects.append(policy_target_object)

            return rest_utils.JSONResponse(policy_target_objects)
        except Exception:
            msg = _("Failed to retrieve groups")
            LOG.error(msg)
            exceptions.handle(request, msg, redirect=shortcuts.redirect)


class Members(generic.View):
    optional_arguments = [
        'block_device_mapping',
        'block_device_mapping_v2',
        'availability_zone',
        'admin_pass', 'disk_config',
        'config_drive', "scheduler_hints"
    ]

    @rest_utils.ajax()
    def post(self, request):
        instance_count = request.DATA['instance_count']

        try:
            if instance_count == 1:
                self.create_instance(request)
            elif instance_count > 1:
                for i in range(0, instance_count):
                    self.create_instance(request, "_" + str(i))

        except Exception:
            instance_name = request.DATA['name']

            error = _("Unable to launch member %(count)s with name %(name)s")
            message = error % {
                'count': instance_count,
                'name': instance_name
            }
            LOG.exception(message)

            raise rest_utils.AjaxError(400, message)

        return rest_utils.CreatedResponse('/api/nova/servers/%s')

    def create_instance(self, request, suffix=""):
        # Instances need to be created one by one, because each instance
        # needs to have it's own GBP port
        kw = {
            'instance_count': 1
        }

        # Mandatory creation arguments and port creation
        try:
            instance_name = request.DATA['name'] + suffix

            if "group_policy_targets" in request.DATA and (
                    request.DATA["group_policy_targets"]):
                meta_data, nics = self.create_ports(request, instance_name)

                kw['meta'] = meta_data
                kw['nics'] = nics
            else:
                if 'nics' in request.DATA:
                    kw['nics'] = request.DATA['nics']
                if 'meta' in request.DATA:
                    kw['meta'] = request.DATA['meta']

            args = (
                request,
                instance_name,
                request.DATA['source_id'],
                request.DATA['flavor_id'],
                request.DATA['key_name'],
                request.DATA['user_data'],
                request.DATA['security_groups'],
            )

        except KeyError as e:
            raise rest_utils.AjaxError(400, 'Missing required parameter '
                                            "'%s'" % e.args[0])

        # Optional creation arguments
        for name in self.optional_arguments:
            if name in request.DATA:
                kw[name] = request.DATA[name]

        return api.nova.server_create(*args, **kw)

    # 1) Missing request.DATA entries get propagated to 'create_instance' as
    #    KeyError
    # 2) All other errors are propagated to 'post' as generic failure Exception
    @staticmethod
    def create_ports(request, instance_name):
        nics = []
        pts = []

        for policy_target_id in request.DATA["group_policy_targets"]:
            policy_target = client.policy_target_get(request,
                                                     policy_target_id['id'])

            args = {
                'policy_target_group_id': policy_target.id,
                'name': instance_name[:41] + "_gbpui"
            }

            for subnet_id in policy_target.subnets:
                subnet = api.neutron.subnet_get(request, subnet_id)

                if 'fixed_ip' in policy_target_id and IPAddress(
                        policy_target_id['fixed_ip']) in \
                        IPNetwork(subnet['cidr']):
                    args['fixed_ips'] = [{
                        'subnet_id': subnet['id'],
                        'ip_address': policy_target_id['fixed_ip']
                    }]

            port = client.pt_create(request, **args)

            nics.append({
                'port-id': port.port_id
            })
            pts.append(port.id)

        meta_data = {'pts': ','.join(pts)}

        return meta_data, nics
