import logging
import copy
from collections import namedtuple, UserDict
from io import StringIO
from contextlib import closing

PluginConfigInfo = namedtuple("PluginConfigInfo", ["name", "properties", "fast_id", "enabled"])

#safe print (masked password) plugin properties
class PluginProperties(UserDict):

    mask_keys = ['password', 'secret', 'private']

    @staticmethod
    def __get_string_key(properties):
        for key in properties.keys():
            if isinstance(key, str):
                lower = key.lower()
                is_masked = next((True for x in PluginProperties.mask_keys if lower.find(x)>-1), False)
                if is_masked:
                    yield key
                    continue
            yield None
    
    def __init__(self, base_dict: dict, meta_properties = None):
        UserDict.__init__(self, base_dict)
        self.to_mask = set()
        if meta_properties:
            self.to_mask = {meta_property['key'] for meta_property in meta_properties 
                            if meta_property.get('type') == 'Password'}
        else:
            self.to_mask = { key for key in PluginProperties.__get_string_key(base_dict) 
                            if key != None}
    
    def __repr__(self):
        return self.__str__()
    
    def __str__(self):
        with closing(StringIO()) as output:
            output.write('{')
            first = True
            for k, v in self.items():
                if first:
                    first = False
                else:
                    output.write(', ')
                if k in self.to_mask:
                    output.write('{!r}: \'########\''.format(k))
                else:    
                    output.write('{!r}: {!r}'.format(k,v))
            output.write('}')
            return output.getvalue()
        
        
def create_config(plugin_info, received_configs, logger = logging.getLogger(__name__)):
    # we always expect metadata for the plugin to be present
    plugin_name = plugin_info.reported_name
    meta = plugin_info.json_data
    meta_properties = meta.get('properties', [])
    config = PluginProperties({}, meta_properties)
    missing_properties = set()
    for property in meta_properties:
        if 'defaultValue' in property:
            config[property['key']] = property['defaultValue']
        else:
            missing_properties.add(property['key'])

    received_config = received_configs.get(plugin_name, None)
    fast_id = ()
    # we cannot know if plugin is enabled or disabled until we receive at least one configuration response
    # therefore even if plugin does not require configuration, we must wait until we receive config
    enabled = plugin_info.is_in_development
    if received_config:
        for property_name, property_value in received_config.properties.items():
            if property_value is not None:
                config[property_name] = property_value
                missing_properties.discard(property_name)
        if not plugin_info.is_in_development:
            enabled = received_config.enabled
        fast_id = received_config.fast_id
        if not enabled:
            logger.debug(f'Received disabled config for plugin {plugin_info}')
    if logger.isEnabledFor(logging.DEBUG) and len(missing_properties) > 0:
        logger.error(f'Incomplete config for plugin: {plugin_info}, missing_properties: {missing_properties}')
    return config, enabled, len(missing_properties) == 0, fast_id
