#
# Copyright (c) 2019 Juniper Networks, Inc. All rights reserved.
#

"""L3 Gateway Feature Implementation."""

from builtins import str
from collections import OrderedDict

from abstract_device_api.abstract_device_xsd import (
    Feature, RoutingInstance, Vlan
)

from .db import LogicalRouterDM, VirtualMachineInterfaceDM, VirtualNetworkDM
from .dm_utils import DMUtils
from .feature_base import FeatureBase

import gevent # noqa


class L3GatewayFeature(FeatureBase):

    @classmethod
    def feature_name(cls):
        return 'l3-gateway'
    # end feature_name

    def __init__(self, logger, physical_router, configs):
        """L3 Gateway Feature"""
        self.pi_map = None
        super(L3GatewayFeature, self).__init__(
            logger, physical_router, configs)
    # end __init__

    def _get_connected_vn_ids(self):
        return self._get_connected_vns('l3')
    # end _get_connected_vn_ids

    def _build_ri_config(self, vn, ri_name, ri_obj, export_targets,
                         import_targets, feature_config, irb_ips,
                         erb_pr_role):
        gevent.idle()
        network_id = vn.vn_network_id
        vxlan_id = vn.get_vxlan_vni()
        desc = DMUtils.vn_comment(vn)
        is_master_vn = False
        lr = None
        if vn.logical_router is None:
            # try updating logical router incase of DM restart
            vn.set_logical_router(vn.fq_name[-1])
        lr_uuid = vn.logical_router
        if lr_uuid:
            lr = LogicalRouterDM.get(lr_uuid)
            if lr:
                if lr.is_master is True:
                    is_master_vn = True
        ri = RoutingInstance(
            name=ri_name, virtual_network_mode='l3',
            export_targets=export_targets, import_targets=import_targets,
            virtual_network_id=str(network_id), vxlan_id=str(vxlan_id),
            is_public_network=vn.router_external, routing_instance_type='vrf',
            is_master=is_master_vn,
            comment=desc)

        for prefix in vn.get_prefixes(self._physical_router.uuid):
            ri.add_prefixes(self._get_subnet_for_cidr(prefix))

        _, li_map = self._add_or_lookup_pi(self.pi_map, 'irb', 'irb')
        irb = None
        if irb_ips:
            irb = self._add_or_lookup_li(li_map, 'irb.' + str(network_id),
                                         network_id)
            if vn.has_ipv6_subnet is True:
                irb.set_is_virtual_router(True)
            for (irb_ip, gateway) in irb_ips:
                self._add_ip_address(irb, irb_ip, gateway=gateway)
            # This is a hack to advertise vrouter IP in underlay ebgp
            # Required in ERB-UCAST-GW when a VN is directly extended to a PR
            # Refererence: CEM-20163
            if erb_pr_role and not lr:
                irb.set_comment("PR External IRB")
            elif lr and lr.is_master:
                irb.set_comment("Master LR IRB")

        vlan = Vlan(name=DMUtils.make_bridge_name(vxlan_id), vxlan_id=vxlan_id)
        vlan.set_description(desc)
        feature_config.add_vlans(vlan)
        if irb:
            self._add_ref_to_list(vlan.get_interfaces(), irb.get_name())
        if erb_pr_role:
            # for ERB_xxx_gateway PR role, set vlan id for current vn
            self._set_vn_vlanid(vlan, vn)
        return ri
    # end _build_ri_config

    def _set_vn_vlanid(self, vlan, vn):
        for vmi_uuid in vn.virtual_machine_interfaces or []:
            vmi = VirtualMachineInterfaceDM.get(vmi_uuid)
            if vmi is None or vmi.is_device_owner_bms() is False:
                continue
            if int(vmi.vlan_tag) == 0:
                vlan_tag = str(vmi.port_vlan_tag)
            else:
                vlan_tag = str(vmi.vlan_tag)
            if int(vlan_tag) > 0:
                vlan.set_vlan_id(int(vlan_tag))
                # for enterprise style only one vlan id valid per VN.
                # for SP style, multiple vlanid allowed per VPG for same VN
                # though abstract config has single vlan id, so if junos
                # allows change abstract config's this vlan_id as a list,
                # until junos team confirms, lets break here on first vlanid
                break
    # end _set_vn_vlanid

    def feature_config(self, **kwargs):
        self.pi_map = OrderedDict()
        feature_config = Feature(name=self.feature_name())
        vns = self._get_connected_vn_ids()
        # use_gateway_ip = all(
        #     [c.additional_params.use_gateway_ip == 'True'
        #         for c in self._configs])
        erb_pr_role = self._physical_router.is_erb_only()
        # To make this code in line with monolithic code.
        use_gateway_ip = erb_pr_role

        if erb_pr_role:
            vn_li_map = self._get_vn_li_map('l2')
            vpg_vn_uuids = list(vn_li_map.keys())
            vns = list(set(vns) & set(vpg_vn_uuids))

        irb_ip_map = self._physical_router.allocate_irb_ips_for(
            vns, use_gateway_ip)
        for vn_uuid in vns:
            vn_obj = VirtualNetworkDM.get(vn_uuid)
            ri_obj = self._get_primary_ri(vn_obj)
            if ri_obj is None:
                continue
            ri_name = DMUtils.make_vrf_name(vn_obj.fq_name[-1],
                                            vn_obj.vn_network_id, 'l3')
            export_targets, import_targets = self._get_export_import_targets(
                vn_obj, ri_obj)
            ri = self._build_ri_config(
                vn_obj, ri_name, ri_obj, export_targets,
                import_targets, feature_config, irb_ip_map.get(vn_uuid, []),
                erb_pr_role)
            feature_config.add_routing_instances(ri)

        for pi, li_map in list(self.pi_map.values()):
            pi.set_logical_interfaces(list(li_map.values()))
            feature_config.add_physical_interfaces(pi)

        return feature_config
    # end push_conf

# end L3GatewayFeature
