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

"""
VNC service management for kubernetes
"""

import uuid

from vnc_api.vnc_api import *
from config_db import *
from kube_manager.common.kube_config_db import NamespaceKM
from kube_manager.common.kube_config_db import NetworkPolicyKM
from vnc_kubernetes_config import VncKubernetesConfig as vnc_kube_config
from vnc_common import VncCommon

class VncNetworkPolicy(VncCommon):

    def __init__(self):
        super(VncNetworkPolicy,self).__init__('NetworkPolicy')
        self._name = type(self).__name__
        self._queue = vnc_kube_config.queue()
        self._ingress_ns_label_cache = {}
        self._ingress_pod_label_cache = {}
        self._np_pod_label_cache = {}
        self._default_ns_sgs = {}
        self._vnc_lib = vnc_kube_config.vnc_lib()
        self._label_cache = vnc_kube_config.label_cache()
        self._build_np_cache()
        self._logger = vnc_kube_config.logger()
        self._logger.info("VncNetworkPolicy init done.")

    def _build_np_cache(self):
        ns_uuid_set = set(NamespaceKM.keys())
        ns_sg_name_set = set()
        for ns_uuid in ns_uuid_set or []:
            ns = NamespaceKM.get(ns_uuid)
            if not ns:
                continue
            ns_name = ns.name
            ns_sg = "-".join(
                [vnc_kube_config.cluster_name(), ns_name, 'sg'])
            ns_sg_name_set.add(ns_sg)
            default_sg = "-".join(
                [vnc_kube_config.cluster_name(), ns_name, 'default'])
            ns_sg_name_set.add(default_sg)
            self._default_ns_sgs[ns_name] = {}
        sg_uuid_set = set(SecurityGroupKM.keys())
        for sg_uuid in sg_uuid_set or []:
            sg = SecurityGroupKM.get(sg_uuid)
            if not sg or not sg.namespace:
                continue
            if sg.name in ns_sg_name_set:
                sg_dict = {}
                sg_dict[sg.name] = sg_uuid
                self._default_ns_sgs[sg.namespace].update(sg_dict)
            elif sg.np_pod_selector:
                self._update_sg_cache(self._np_pod_label_cache,
                            sg.np_pod_selector, sg.uuid)
            elif sg.ingress_pod_selector:
                self._update_sg_cache(self._ingress_pod_label_cache,
                            sg.ingress_pod_selector, sg.uuid)
            if sg.np_spec:
                #_get_ingress_rule_list update _ingress_ns_label_cache
                self._get_ingress_rule_list(sg.np_spec,
                            sg.namespace, sg.name, sg.uuid)

    def _get_ns_allow_all_label(self):
        label = {'NS-SG':'ALLOW-ALL'}
        return label

    def _find_namespaces(self, labels, ns_set=None):
        result = set()
        for label in labels.items():
            key = self._label_cache._get_key(label)
            ns_ids = self._label_cache.ns_label_cache.get(key, set())
            #no matching label
            if not ns_ids:
                return ns_ids
            if not result:
                result = ns_ids.copy()
            else:
                result.intersection_update(ns_ids)

        if ns_set:
            result.intersection_update(ns_set)
        return result

    def _find_pods(self, labels, pod_set=None):
        result = set()
        for label in labels.items():
            key = self._label_cache._get_key(label)
            pod_ids = self._label_cache.pod_label_cache.get(key, set())
            #no matching label
            if not pod_ids:
                return pod_ids
            if not result:
                result = pod_ids.copy()
            else:
                result.intersection_update(pod_ids)

        if pod_set:
            result.intersection_update(pod_set)
        return result

    def _find_sg(self, sg_cache, labels):
        result = set()
        for label in labels.items():
            key = self._label_cache._get_key(label)
            sg_ids = sg_cache.get(key, set())
            #no matching label
            if not sg_ids:
                continue
            if not result:
                result = sg_ids.copy()
            else:
                result.update(sg_ids)
        return result

    def _clear_sg_cache_uuid(self, sg_cache, sg_uuid):
        if not sg_uuid:
            return
        key_list = [k for k,v in sg_cache.items() if sg_uuid in v]
        for key in key_list or []:
            label = tuple(key.split(':'))
            self._label_cache._remove_label(key, sg_cache, label, sg_uuid)

    def _clear_sg_cache(self, sg_cache, labels, sg_uuid):
        if not labels or not sg_uuid:
            return
        for label in labels.items() or []:
            key = self._label_cache._get_key(label)
            self._label_cache._remove_label(key, sg_cache, label, sg_uuid)

    def _update_sg_cache(self, sg_cache, labels, sg_uuid):
        if not labels or not sg_uuid:
            return
        for label in labels.items() or []:
            key = self._label_cache._get_key(label)
            self._label_cache._locate_label(key, sg_cache, label, sg_uuid)

    def _set_sg_annotations(self, namespace, name, sg_obj, **kwargs):
        SecurityGroupKM.add_annotations(self, sg_obj, namespace, sg_obj.name,
            **kwargs)
        return

    def _vnc_create_sg(self, np_spec, namespace, name,
            uuid=None, **kwargs_annotations):
        proj_fq_name = vnc_kube_config.cluster_project_fq_name(namespace)
        proj_obj = Project(name=proj_fq_name[-1], fq_name=proj_fq_name,
            parent='domain')
        sg_obj = SecurityGroup(name=name, parent_obj=proj_obj)
        if uuid:
            sg_obj.uuid = uuid
        if np_spec:
            kwargs_annotations.update({'np_spec': json.dumps(np_spec)})
        self._set_sg_annotations(namespace, name,
            sg_obj, **kwargs_annotations)
        try:
            self._vnc_lib.security_group_create(sg_obj)
        except Exception as e:
            self._logger.error("%s - %s SG Not Created" %s(self._name, name))
            return None
        sg = SecurityGroupKM.locate(sg_obj.uuid)
        return sg

    def _create_ingress_sg(self, namespace, sg_name, ingress_pod_selector):
        sg = self._vnc_create_sg(None, namespace,
                sg_name, ingress_pod_selector=ingress_pod_selector)
        return sg

    def _create_np_sg(self, spec, namespace, name, uuid, np_pod_selector):
        sg_name = VncCommon.make_name(name, uuid)
        sg = self._vnc_create_sg(spec, namespace,
                sg_name, uuid, np_pod_selector=np_pod_selector)
        return sg

    def _get_rule_list(self, address_list, port_list, ingress=True):
        rule_list = []
        if ingress:
            target_address = 'src_address'