#!/usr/bin/env python
from __future__ import print_function
import copy
import functools
from glob import glob
import importlib
import logging
import os
import re
import sqlite3
import sys
import time
import traceback
import json

from slackrtm import SlackClient
from slackrtm.server import SlackConnectionError, SlackLoginError
from .server import LimboServer
from .fakeserver import FakeServer

CURDIR = os.path.abspath(os.path.dirname(__file__))
DIR = functools.partial(os.path.join, CURDIR)

PYTHON3 = sys.version_info[0] > 2

logger = logging.getLogger(__name__)

class InvalidPluginDir(Exception):
    def __init__(self, plugindir):
        message = "Unable to find plugin dir {0}".format(plugindir)
        super(InvalidPluginDir, self).__init__(message)

def init_log(config):
    loglevel = config.get("loglevel", logging.INFO)
    logformat = config.get("logformat", '%(asctime)s:%(levelname)s:%(name)s:%(message)s')
    if config.get("logfile"):
        logging.basicConfig(filename=config.get("logfile"), format=logformat, level=loglevel)
    else:
        logging.basicConfig(format=logformat, level=loglevel)

def strip_extension(lst):
    return (os.path.splitext(l)[0] for l in lst)

def init_plugins(plugindir, plugins_to_load=None):
    if plugindir and not os.path.isdir(plugindir):
        raise InvalidPluginDir(plugindir)

    if not plugindir:
        plugindir = DIR("plugins")

    logger.debug("plugindir: {0}".format(plugindir))

    if os.path.isdir(plugindir):
        pluginfiles = glob(os.path.join(plugindir, "[!_]*.py"))
        plugins = strip_extension(os.path.basename(p) for p in pluginfiles)
    else:
        # we might be in an egg; try to get the files that way
        logger.debug("trying pkg_resources")
        import pkg_resources
        try:
            plugins = strip_extension(
                    pkg_resources.resource_listdir(__name__, "plugins"))
        except OSError:
            raise InvalidPluginDir(plugindir)

    hooks = {}

    oldpath = copy.deepcopy(sys.path)
    sys.path.insert(0, plugindir)

    for plugin in plugins:
        if plugins_to_load and plugin not in plugins_to_load:
            logger.debug("skipping plugin {0}, not in plugins_to_load {1}".format(plugin, plugins_to_load))
            continue

        logger.debug("plugin: {0}".format(plugin))
        try:
            mod = importlib.import_module(plugin)
            modname = mod.__name__
            for hook in re.findall("on_(\w+)", " ".join(dir(mod))):
                hookfun = getattr(mod, "on_" + hook)
                logger.debug("plugin: attaching %s hook for %s", hook, modname)
                hooks.setdefault(hook, []).append(hookfun)

            if mod.__doc__:
                # firstline = mod.__doc__.split('\n')[0]
                part_attachment = json.loads(mod.__doc__)
                hooks.setdefault('help', {})[modname] = part_attachment
                hooks.setdefault('extendedhelp', {})[modname] = mod.__doc__

        # bare except, because the modules could raise any number of errors
        # on import, and we want them not to kill our server
        except:
            logger.warning("import failed on module {0}, module not loaded".format(plugin))
            logger.warning("{0}".format(sys.exc_info()[0]))
            logger.warning("{0}".format(traceback.format_exc()))

    sys.path = oldpath
    return hooks

def run_hook(hooks, hook, *args):
    responses = []
    for hook in hooks.get(hook, []):
        try:
            h = hook(*args)
            if h:
                responses.append(h)
        except:
            logger.warning("Failed to run plugin {0}, module not loaded".format(hook))
            logger.warning("{0}".format(sys.exc_info()[0]))
            logger.warning("{0}".format(traceback.format_exc()))
    return responses

def handle_bot_message(event, server):
    try:
        bot = server.slack.server.bots[event["bot_id"]]
    except KeyError:
        logger.debug("bot_message event {0} has no bot".format(event))
        return

    return "\n".join(run_hook(server.hooks, "bot_message", event, server))

def handle_message(event, server):
    subtype = event.get("subtype", "")
    if subtype == "message_changed":
        return

    if subtype == "bot_message":
        return handle_bot_message(event, server)

    try:
        msguser = server.slack.server.users[event["user"]]
    except KeyError:
        logger.debug("event {0} has no user".format(event))
        return

    return "\n".join(run_hook(server.hooks, "message", event, server))


def handle_channel_joined(event, server):
    temp = "".join(run_hook(server.hooks, "channel_joined", event, server))
    return temp


event_handlers = {
    'message': handle_message,
    'channel_joined': handle_channel_joined
}

def handle_event(event, server):
    handler = event_handlers.get(event.get("type"))
    if handler:
        return handler(event, server)

def getif(config, name, envvar):
    if envvar in os.environ:
        config[name] = os.environ.get(envvar)

def init_config():
    config = {}
    getif(config, "token", "SLACK_TOKEN")
    getif(config, "loglevel", "LIMBO_LOGLEVEL")
    getif(config, "logfile", "LIMBO_LOGFILE")
    getif(config, "logformat", "LIMBO_LOGFORMAT")
    getif(config, "plugins", "LIMBO_PLUGINS")

    return config

def loop(server, test_loop=None):