"""
Component that will handle the storing of details in xmpp for configuration.
"""
import logging

from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin

logger = logging.getLogger(__name__)


class ConfigurationStanza(ElementBase):
    """
    Stanza responsible for handling the configuration core.
    <configuration xmlns='rho:configuration'>
        <entry>
          <key>some_key</key>
          <value>some_value</value>
        </entry>
    </configuration>
    """
    name = 'configuration'
    namespace = 'rho:configuration'
    plugin_attrib = 'configuration'
    interfaces = {}
    sub_interfaces = {}

    def add_entry(self, key, value):
        entry_stanza = EntryStanza()
        entry_stanza['key'] = str(key)
        entry_stanza['value'] = str(value)
        self.append(entry_stanza)


class EntryStanza(ElementBase):
    """
    Entry Stanza.
    """
    name = 'entry'
    namespace = 'rho:configuration'
    plugin_attrib = 'entry'
    plugin_multi_attrib = 'entries'
    interfaces = {'key', 'value'}
    sub_interfaces = interfaces


class BotConfiguration(base_plugin):

    CONFIGURATION_RECEIVED_EVENT = 'rho::configuration_received'
    CONFIGURATION_UPDATED_EVENT = 'rho::configuration_updated'
    _configuration_data_node = 'rho:configuration'
    name = 'rho_bot_configuration'
    dependencies = {'xep_0060', 'rho_bot_scheduler', }
    description = 'RHO: Configuration Plugin'

    def plugin_init(self):
        """
        Configure the plugin to handle the private storage of data.
        :return:
        """
        self._configuration = dict()
        register_stanza_plugin(ConfigurationStanza, EntryStanza, iterable=True)
        self.xmpp.add_event_handler("session_start", self._start)

    def _start(self, event):
        """
        When connected to the service, request the configuration details for the object.  When finished, notify all
        listeners that configuration details have been fetched from the server.
        :return:
        """
        promise = self.xmpp['rho_bot_scheduler'].promise()
        self.xmpp['xep_0060'].get_nodes(jid=self.xmpp.boundjid.bare,
                                        callback=self.xmpp['rho_bot_scheduler'].generate_callback_promise(promise))
        promise = promise.then(self._found_nodes)
        promise = promise.then(None, self._create_node)
        promise = promise.then(self._fetch_configuration)
        promise.then(self._configuration_data_retrieved)

    def _found_nodes(self, stanza):
        """
        Check to see if the configuration node is defined or not.  If it's not defined, then create it.
        :param stanza:
        :return:
        """
        promise = self.xmpp['rho_bot_scheduler'].promise()

        logger.info('Found Nodes: %s' % stanza)

        found = False
        for item in stanza['disco_items']['items']:
            if item[1] == self._configuration_data_node:
                found = True
                break

        if found:
            promise.resolved(None)
        else:
            promise.rejected('Node not found')

        return promise

    def _fetch_configuration(self, ignored):
        """
        Request that the configuration be loaded.
        :return:
        """
        promise = self.xmpp['rho_bot_scheduler'].promise()
        logger.info('Fetching Configuration: %s' % self._configuration_data_node)
        self.xmpp['xep_0060'].get_items(jid=self.xmpp.boundjid.bare, node=self._configuration_data_node,
                                        callback=self.xmpp['rho_bot_scheduler'].generate_callback_promise(promise))

        return promise

    def _create_node(self, ignored):
        """
        Create the configuration storage node, and then store the data.
        :return:
        """
        promise = self.xmpp['rho_bot_scheduler'].promise()

        logger.info('Creating node: %s' % self._configuration_data_node)

        configuration_form = self.xmpp['xep_0004'].make_form(ftype='submit', title='Node Configuration')
        configuration_form.add_field(var='pubsub#access_model', value='whitelist')
        configuration_form.add_field(var='pubsub#persist_items', value='1')