import logging
import os
import pluggy
import importlib
import asyncio

from sirbot import Plugin

from .__meta__ import DATA as METADATA
from . import hookspecs
from .api import RTMClient, HTTPClient
from .dispatcher import SlackMainDispatcher
from .user import SlackUserManager
from .channel import SlackChannelManager
from .facade import SlackFacade
from .errors import SlackClientError

logger = logging.getLogger('sirbot.slack')

MANDATORY_PLUGIN = ['sirbot_plugin_slack.user', 'sirbot_plugin_slack.channel']


class SirBotSlack(Plugin):
    __name__ = METADATA['name']
    __version__ = METADATA['version']

    def __init__(self, loop):
        super().__init__(loop)
        logger.debug('Initializing slack plugin')
        self._loop = loop
        self._config = None
        self._facades = None
        self._session = None

        self._token = None
        self._dispatcher = None
        self._rtm_client = None
        self._http_client = None
        self._users = None
        self._channels = None

    @property
    def started(self):
        if self._dispatcher:
            return self._dispatcher.started
        return False

    async def configure(self, config, router, session, facades):
        logger.debug('Configuring slack plugin')
        self._config = config
        self._session = session
        self._facades = facades

        self._token = os.environ.get('SIRBOT_SLACK_TOKEN', '')
        if not self._token:
            raise EnvironmentError(
                'SIRBOT_SLACK_TOKEN environment variable is not set')

        self._http_client = HTTPClient(token=self._token, loop=self._loop,
                                       session=self._session)
        self._users = SlackUserManager(self._http_client)
        self._channels = SlackChannelManager(self._http_client)
        pm = self._initialize_plugins()
        self._dispatcher = SlackMainDispatcher(
            http_client=self._http_client,
            users=self._users,
            channels=self._channels,
            pm=pm,
            facades=facades,
            loop=self._loop,
            config=self._config
        )
        self._rtm_client = RTMClient(token=self._token, loop=self._loop,
                                     callback=self.incoming,
                                     session=self._session)

    def facade(self):
        """
        Initialize and return a new facade

        This is called by the core when a for each incoming message and when
        another plugin request a slack facade
        """
        return SlackFacade(self._http_client, self._users,
                           self._channels, self._dispatcher.bot_id)

    async def start(self):
        logger.debug('Starting slack plugin')
        await self._create_db_table()
        await self._rtm_client.connect()

    async def _reconnect(self):
        logger.warning('Trying to reconnect to slack')
        try:
            await self._rtm_client.connect()
        except SlackClientError:
            await asyncio.sleep(1, loop=self._loop)
            await self._reconnect()

    async def incoming(self, msg):
        msg_type = msg.get('type', None)

        if msg_type in ('team_migration_started', 'goodbye'):
            logger.debug('Bot needs to reconnect')
            await self._reconnect()
        else:
            await self._dispatcher.incoming(msg, msg_type)

    def _initialize_plugins(self):
        """
        Import and register the plugins

        Most likely composed of functions reacting to events and messages
        """
        logger.debug('Initializing plugins of slack plugin')
        pm = pluggy.PluginManager('sirbot.slack')
        pm.add_hookspecs(hookspecs)

        for plugin in MANDATORY_PLUGIN + self._config.get('plugins', []):
            p = importlib.import_module(plugin)
            pm.register(p)

        return pm

    async def database_update(self, metadata, db):

        if metadata['version'] == '0.0.1':
            await db.execute(
                '''ALTER TABLE slack_messages ADD COLUMN type TEXT''')
            metadata['version'] = '0.0.2'

        if metadata['version'] == '0.0.2':
            await db.execute(
                '''ALTER TABLE slack_messages ADD COLUMN attachment INT''')
            metadata['version'] = '0.0.3'

        if metadata['version'] == '0.0.3':
            await db.execute(
                '''ALTER TABLE slack_messages ADD COLUMN raw TEXT''')
            metadata['version'] = '0.0.4'

        if metadata['version'] == '0.0.4':
            await db.execute('''ALTER TABLE slack_messages
                                 RENAME TO slack_messages_tmp''')

            await db.execute('''CREATE TABLE IF NOT EXISTS slack_messages (
            ts REAL,
            channel TEXT,
            user TEXT,
            text TEXT,
            type TEXT,
            attachment INT,
            raw TEXT,
            conversation_id REAL,
            PRIMARY KEY (ts, channel)
            )
            ''')

            await db.execute('''INSERT INTO slack_messages(ts, channel, user,
                                 text, type, attachment, raw, conversation_id)
                                 SELECT * FROM slack_messages_tmp''')

            await db.execute('''DROP TABLE slack_messages_tmp''')

            metadata['version'] = '0.0.5'

        return self.__version__

    async def _create_db_table(self):
        db = self._facades.get('database')
        await db.execute('''CREATE TABLE IF NOT EXISTS slack_users (
        id TEXT PRIMARY KEY NOT NULL,
        dm_id TEXT,
        admin BOOLEAN DEFAULT FALSE
        )
        ''')

        await db.execute('''CREATE TABLE IF NOT EXISTS slack_channels (
        id TEXT PRIMARY KEY NOT NULL,
        name TEXT UNIQUE,
        is_member BOOLEAN,
        is_archived BOOLEAN
        )
        ''')

        await db.execute('''CREATE TABLE IF NOT EXISTS slack_messages (
        ts REAL,
        channel TEXT,
        user TEXT,
        text TEXT,
        type TEXT,
        attachment INT,
        raw TEXT,
        conversation_id REAL,
        PRIMARY KEY (ts, channel)
        )
        ''')

        await db.set_plugin_metadata(self)
        await db.commit()

@@ -5150,25 +5150,8 @@
  raw
-, conversation_id
 )%0A  

Fix database update