import collections, time, logging
from typing import List
from ruxit.remote.remote_plugin_engine import RemotePluginEngine
from ruxit.tools.limit_checker import LimitChecker

RemoteTopologyInfo = collections.namedtuple(
    "RemoteTopologyInfo",
    ["contentType", "timestamp", "customDeviceGroups"]
)
CustomDeviceGroup = collections.namedtuple(
    "CustomDeviceGroup",
    ["groupEntityId", "groupName", "customDevices", "technologies", "reportingPluginConfigId", "endpoints"]
)
CustomDevice = collections.namedtuple(
    "CustomDevice",
    ["entityId", "name", "stateTransitions", "technologies", "endpoints"]
)

Endpoint = collections.namedtuple(
    "Endpoint",
    ["ips", "single_port", "dnsNames", "port_list", "port_range"]
)

class PluginCustomDeviceReporter:
    '''
    This class is a bridge to the native code that's used to report the topology information.
    '''
    def __init__(self, external_api):
        self._external_api = external_api
        self._last_reported_devices = {}
        self._DEVICES_LIMIT = 5000
        self._GROUPS_LIMIT = 500

    @property
    def _devices_limit(self):
        return self._external_api.get_int_debug_flag("debugPluginAgentDevicesLimitNative", self._DEVICES_LIMIT)

    @property
    def _groups_limit(self):
        return self._external_api.get_int_debug_flag("debugPluginAgentGroupsLimitNative", self._GROUPS_LIMIT)


    def report_devices(self, engine: RemotePluginEngine):
        """
        Extracts the topology information from the passed engines.

        :param engines: A list of remote plugin engines in the agent.
        """
        if not engine.topology_builder:
            return

        logger = logging.getLogger()
        groups = []
        # gather all topology groups from all plugins
        device_limit = LimitChecker(lambda: self._devices_limit, engine.device_counter)
        group_limit = LimitChecker(lambda: self._groups_limit, engine.group_counter)

        groups_from_topology, config_id = engine.flush_topology()
        for (group_id, group) in groups_from_topology.items():
            if group_limit.limit_reached: break

            # gather all nodes from one plugin
            devices = []
            for node_name, node in group.get_elements().items():
                if device_limit.limit_reached: break

                endpoints = []
                # gather all endpoints from one node
                for endpoint in node.endpoints:
                    ip, port, dns_names, port_list, port_range = endpoint
                    endpoints.append(Endpoint([ip], port, dns_names, port_list, port_range))

                devices.append(CustomDevice(node.id, node.name, ["EXPLICIT_STATE_MONITORED"], [node.technology], endpoints))
                device_limit.increment()

            group_endpoints = []
            groups.append(
                CustomDeviceGroup(group.id, group.name, devices, [group.technology], config_id, group_endpoints))
            if not group._is_reported:
                group_limit.increment()

        engine.device_counter = device_limit.counter
        engine.group_counter = group_limit.counter

        topology_info = RemoteTopologyInfo("REMOTE_PLUGINS_TOPOLOGY_INFO", 
                                           self._external_api.get_timestamp(), 
                                           groups)
        logger.debug("Reporting topology %s: %s", engine, topology_info)
        self._external_api.report_topology(topology_info)

        if device_limit.limit_reached:
            engine.add_limit_exceeded(
                f"Number of devices exceeded allowed threshold: {device_limit.limit}" )
        if group_limit.limit_reached:
            engine.add_limit_exceeded(
                f"Number of groups exceeded allowed threshold: {group_limit.limit}")