from collections import namedtuple
import json
from logging import getLogger
from typing import List, Any, Set
from ruxit.platform.platform_api import PlatformAPI, SelectedPlugins, PluginConfig
from ruxit.select_plugins import PluginSelector
from ruxit.entity_resolver import EntityResolver
from ruxit.plugin_state_machine import PluginEngine
from ruxit.config_utils import PluginConfigInfo, create_config, PluginProperties
from ruxit.package_utils.plugin_updater import PluginInfo

ActivatedPlugin = namedtuple("ActivatedPlugin", ["activation_context", "plugin_info"])


# engine customization for PA
class EmbeddedAPI(PlatformAPI):

    def __init__(self, external_api):
        self._external_api = external_api
        self._latest_process_snapshot = None
        self._latest_plugin_configs = {}
        self._plugin_selector = PluginSelector()
        self._entity_resolver = EntityResolver()
        self._plugin_dev_global_logger = getLogger("plugin_development.global")

    @property
    def external_api(self):
        return self._external_api

    @property
    def entity_resolver(self):
        return self._entity_resolver

    def _update_latest_snapshot(self):
        process_snapshot = None
        try:
            process_snapshot = self._external_api.get_latest_snapshot()
        except:
            self._plugin_dev_global_logger.exception("Unable to get process snapshot")
        if process_snapshot and (self._latest_process_snapshot != process_snapshot):
            self._plugin_dev_global_logger.debug("Latest process snapshot: %s", process_snapshot)
            self._entity_resolver.set_snapshot(process_snapshot)
            self._latest_process_snapshot = process_snapshot
            return True
        return False

    def _update_latest_configs(self):
        try:
            configs_raw = self._external_api.get_latest_configs()
        except:
            self._plugin_dev_global_logger.exception("Unable to get latest configs")
            return
        configs = {}
        for plugin_name, plugin_config_info in configs_raw.items():
            try:
                plugin_config_raw = plugin_config_info.pluginConfigProperties
                plugin_json = json.loads(plugin_config_raw)
                configs[plugin_name] = PluginConfigInfo(
                    plugin_name,
                    PluginProperties(plugin_json.get('properties', {})),
                    plugin_config_info.fastCheck,
                    plugin_config_info.pluginEnabled
                )
            except Exception:
                self._plugin_dev_global_logger.exception("Unable to parse config for plugin: %s", plugin_name)
        self._latest_plugin_configs = configs

    # update snapshot and configuration
    def data_update(self) -> bool:
        self._update_latest_configs()
        return self._update_latest_snapshot()

    def select_plugins(self, plugin_engines: List[PluginEngine], plugin_metadata: List[PluginInfo]) -> SelectedPlugins:
        to_activate, to_deactivate = self._plugin_selector.select_plugins(
            self._latest_process_snapshot,
            plugin_metadata,
            plugin_engines,
            self._plugin_dev_global_logger
        )
        return SelectedPlugins(to_activate, to_deactivate)

    # get plugin config by name
    def get_plugin_config(self, plugin_engine: PluginEngine, plugin_metadata: List[PluginInfo]) -> PluginConfig:
        plugin_name = plugin_engine.get_name()
        plugin_info_by_name = {pi.name: pi for pi in plugin_metadata}
        val = plugin_info_by_name.get(plugin_name, None)
        if val is None:
            return PluginConfig (None, 0, False)
        cfg, enabled, complete, fast_id = create_config(
            val, self._latest_plugin_configs, self._plugin_dev_global_logger
        )

        if complete:
            return PluginConfig(cfg, fast_id, enabled)
        if fast_id:
            self._plugin_dev_global_logger.info("performing fast_config even though config is not complete")
            return PluginConfig(cfg, fast_id, enabled)
        return PluginConfig(None, 0, False)

    def get_not_existing_plugins(self, plugins):
        #not implemented for local plugins
        return [];

    def additional_report_step(self, plugin_engine: PluginEngine) -> None:
        return

    def create_engine(self, plugin_info: PluginInfo, activation_context) -> Any:
        return PluginEngine(
            external_api=self._external_api,
            entity_resolver=self._entity_resolver,
            plugin_info=plugin_info,
            activation_context=activation_context,
        )

    @property
    def is_local(self):
        return True
