import copy
from common.Constants import (
    AGGREGATED_TOPOLOGY_UUID, DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, DOMAINS_TOPOLOGY_UUID)
from common.DeviceTypes import DeviceTypeEnum
from common.proto.context_pb2 import (
    Context, ContextId, Device, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Topology, TopologyId)
from common.tools.object_factory.Context import json_context, json_context_id
from common.tools.object_factory.Device import json_device, json_device_id
from common.tools.object_factory.Topology import json_topology, json_topology_id
from context.client.ContextClient import ContextClient

def create_interdomain_entities(context_client : ContextClient) -> ContextId:
    existing_context_ids = context_client.ListContextIds(Empty())
    existing_context_uuids = {context_id.context_uuid.uuid for context_id in existing_context_ids.context_ids}

    # Detect local context name (will be used as abstracted device name); exclude DEFAULT_CONTEXT_UUID
    existing_non_admin_context_uuids = copy.deepcopy(existing_context_uuids)
    existing_non_admin_context_uuids.discard(DEFAULT_CONTEXT_UUID)
    if len(existing_non_admin_context_uuids) != 1:
        MSG = 'Unable to identify own domain name. Existing Contexts({:s})'
        raise Exception(MSG.format(str(existing_context_uuids)))
    own_domain_uuid = existing_non_admin_context_uuids.pop()
    own_context_id = ContextId(**json_context_id(own_domain_uuid))

    #if DEFAULT_CONTEXT_UUID not in existing_context_uuids:
    #    context_client.SetContext(Context(**json_context(DEFAULT_CONTEXT_UUID)))
    #admin_context_id = ContextId(**json_context_id(DEFAULT_CONTEXT_UUID))

    # Create topologies "admin", "domains", and "aggregated"
    existing_topology_ids = context_client.ListTopologyIds(own_context_id)
    existing_topology_uuids = {topology_id.topology_uuid.uuid for topology_id in existing_topology_ids.topology_ids}

    topology_uuids = [DEFAULT_TOPOLOGY_UUID, DOMAINS_TOPOLOGY_UUID, AGGREGATED_TOPOLOGY_UUID]
    for topology_uuid in topology_uuids:
        if topology_uuid in existing_topology_uuids: continue
        context_client.SetTopology(Topology(**json_topology(topology_uuid, context_id=own_context_id)))

    return own_context_id

def create_abstracted_device_if_not_exists(context_client : ContextClient, own_context_id : ContextId) -> Device:
    own_domain_uuid = own_context_id.context_uuid.uuid

    # Create device representing abstracted local domain
    existing_device_ids = context_client.ListDeviceIds(Empty())
    existing_device_uuids = {device_id.device_uuid.uuid for device_id in existing_device_ids.device_ids}
    own_abstract_device_id = DeviceId(**json_device_id(own_domain_uuid))
    if own_domain_uuid in existing_device_uuids:
        own_abstract_device = context_client.GetDevice(own_abstract_device_id)
    else:
        own_abstracted_device_uuid = own_domain_uuid
        own_abstract_device = Device(**json_device(
            own_abstracted_device_uuid, DeviceTypeEnum.NETWORK.value,
            DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED,
            endpoints=[], config_rules=[], drivers=[DeviceDriverEnum.DEVICEDRIVER_UNDEFINED]
        ))
        context_client.SetDevice(own_abstract_device)

        # Add own abstracted device to topologies ["domains"]
        topology_uuids = [DOMAINS_TOPOLOGY_UUID]
        for topology_uuid in topology_uuids:
            topology_id = TopologyId(**json_topology_id(topology_uuid, own_context_id))
            topology_ro = context_client.GetTopology(topology_id)
            device_uuids = {device_id.device_uuid.uuid for device_id in topology_ro.device_ids}
            if own_abstracted_device_uuid in device_uuids: continue

            topology_rw = Topology()
            topology_rw.CopyFrom(topology_ro)
            topology_rw.device_ids.add().device_uuid.uuid = own_abstracted_device_uuid
            context_client.SetTopology(topology_rw)

    return own_abstract_device
