from __future__ import unicode_literals, print_function, division
import feedparser
import dataset
from twisted.internet.reactor import callLater
from threading import Thread
import twisted.internet.error
import logging


logger = logging.getLogger("module_rss")
DATABASE = None
updater = None
botref = None
config = {}


def init(bot, testing=False):
    """ Initialize updater """
    global DATABASE
    global config
    global botref
    global updater
    global logger

    if testing:
        DATABASE = dataset.connect("sqlite:///:memory:")
    else:
        DATABASE = dataset.connect("sqlite:///databases/rss.db")

    logger.info("RSS module initialized")
    botref = bot
    config = bot.config.get("rss", {})
    finalize()
    # As there's no signal if this is a rehash or restart
    # update feeds in 30 seconds
    updater = callLater(30, update_feeds)


def finalize():
    """ Finalize updater (rehash etc) so we don't leave an updater running """
    global updater
    global logger
    logger.info("RSS module finalized")
    if updater:
        try:
            updater.cancel()
        except twisted.internet.error.AlreadyCalled:
            pass
    updater = None


def get_feeds(**kwargs):
    """ Get feeds from database """
    return [
        Feed(f["network"], f["channel"], f["id"])
        for f in list(DATABASE["feeds"].find(**kwargs))
    ]


def find_feed(network, channel, **kwargs):
    """ Find specific feed from database """
    f = DATABASE["feeds"].find_one(network=network, channel=channel, **kwargs)
    if not f:
        return
    return Feed(f["network"], f["channel"], f["id"])


def add_feed(network, channel, url):
    """ Add feed to database """
    f = Feed(network=network, channel=channel, url=url)
    return (f.initialized, f.read())


def remove_feed(network, channel, id):
    """ Remove feed from database """
    f = find_feed(network=network, channel=channel, id=int(id))
    if not f:
        return
    DATABASE["feeds"].delete(id=f.id)
    DATABASE["items_%i" % (f.id)].drop()
    return f


def update_feeds(cancel=True, **kwargs):
    # from time import sleep
    """ Update all feeds in the DB """
    global config
    global updater
    global logger
    logger.info("Updating RSS feeds started")
    for f in get_feeds(**kwargs):
        Thread(target=f.update).start()

    # If we get a cancel, cancel the existing updater
    # and start a new one
    # NOTE: Not sure if needed, as atm cancel isn't used in any command...
    if cancel:
        try:
            updater.cancel()
        except twisted.internet.error.AlreadyCalled:
            pass
        updater = callLater(5 * 60, update_feeds)


def command_rss(bot, user, channel, args):
    commands = ["list", "add", "remove", "latest", "update"]

    args = args.split()
    if not args or args[0] not in commands:
        return bot.say(channel, "rss: valid arguments are [%s]" % (", ".join(commands)))

    command = args[0]
    network = bot.network.alias

    # Get latest feed item from database
    # Not needed? mainly for debugging
    # Possibly useful for checking if feed still exists?
    if command == "latest":
        if len(args) < 2:
            return bot.say(channel, 'syntax: ".rss latest <id from list>"')
        feed = find_feed(network=network, channel=channel, id=int(args[1]))
        if not feed:
            return bot.say(channel, "feed not found, no action taken")
        item = feed.get_latest()
        if not item:
            return bot.say(channel, "no items in feed")
        return bot.say(channel, feed.get_item_str(item))

    # List all feeds for current network && channel
    if command == "list":
        feeds = get_feeds(network=network, channel=channel)
        if not feeds:
            return bot.say(channel, "no feeds set up")

        for f in feeds:
            bot.say(channel, "%02i: %s <%s>" % (f.id, f.name, f.url))
        return

    # Rest of the commands are only for admins
    if not bot.factory.isAdmin(user):
        return bot.say(channel, 'only "latest" and "list" available for non-admins')

    # Add new feed for channel
    if command == "add":
        if len(args) < 2:
            return bot.say(channel, 'syntax: ".rss add url"')
        init, items = add_feed(network, channel, url=args[1])
        if not init:
            return bot.say(channel, "feed already added")
        return bot.say(channel, "feed added with %i items" % len(items))

    # remove feed from channel
    if command == "remove":
        if len(args) < 2:
            return bot.say(channel, 'syntax: ".rss remove <id from list>"')
        feed = remove_feed(network, channel, id=args[1])
        if not feed:
            return bot.say(channel, "feed not found, no action taken")
        return bot.say(channel, 'feed "%s" <%s> removed' % (feed.name, feed.url))

    # If there's no args, update all feeds (even for other networks)
    # If arg exists, try to update the feed...
    if command == "update":
        if len(args) < 2:
            bot.say(channel, "feeds updating")
            update_feeds()