"""Access to countdown items through bot commands."""

import logging
import re
import threading
import time

import parsedatetime as pdt
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta
from django.utils import timezone

from countdown.models import CountdownItem
from ircbot.lib import Plugin, most_specific_message
from ircbot.models import IrcChannel

log = logging.getLogger('countdown.ircplugin')


class Countdown(Plugin):
    """Report on countdown items."""

    new_reminder_regex = (r'remind\s+(?P<who>[^\s]+)\s+(?P<when_type>at|in|on)\s+(?P<when>.*?)\s+'
                          r'(and\s+every\s+(?P<recurring_period>.*?)\s+)?'
                          r'(until\s+(?P<recurring_until>.*?)\s+)?'
                          r'(to|that|about|with)\s+(?P<text>.*?)'
                          r'(?=\s+\("(?P<name>.*)"\)|$)')

    def __init__(self, bot, connection, event):
        """Initialize some stuff."""
        self.running_reminders = []
        self.send_reminders = True
        self.server_config = connection.server_config

        t = threading.Thread(target=self.reminder_thread)
        t.daemon = True
        t.start()

        super(Countdown, self).__init__(bot, connection, event)

    def start(self):
        """Set up handlers."""
        self.running_reminders = []
        self.send_reminders = True

        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!countdown\s+list$',
                                                         self.handle_item_list, -20)
        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], r'^!countdown\s+(.+)$',
                                                         self.handle_item_detail, -20)
        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], self.new_reminder_regex,
                                                         self.handle_new_reminder, -50)

        super(Countdown, self).start()

    def stop(self):
        """Tear down handlers."""
        self.running_reminders = []
        self.send_reminders = False

        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_item_list)
        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_item_detail)
        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_new_reminder)

        super(Countdown, self).stop()

    def reminder_thread(self):
        """After IRC is started up, begin sending reminders."""
        # TODO: figure out if we need this sleep, which exists so we don't send reminders while still connecting to IRC
        time.sleep(30)
        while self.send_reminders:
            reminders = CountdownItem.objects.filter(is_reminder=True, sent_reminder=False,
                                                     at_time__lte=timezone.now(),
                                                     reminder_target_new__server=self.server_config)

            for reminder in reminders:
                log.debug("%s @ %s", reminder.reminder_message, reminder.at_time)
                if reminder.at_time <= timezone.now():
                    log.info("sending %s to %s", reminder.reminder_message, reminder.reminder_target_new.name)
                    self.bot.reply(None, reminder.reminder_message, explicit_target=reminder.reminder_target_new.name)

                    # if recurring and not hit until, set a new at time, otherwise stop reminding
                    if reminder.recurring_until is not None and timezone.now() >= reminder.recurring_until:
                        reminder.sent_reminder = True
                    elif reminder.recurring_period != '':
                        calendar = pdt.Calendar()
                        when_t = calendar.parseDT(f'in one {reminder.recurring_period}', reminder.at_time,
                                                  tzinfo=reminder.at_time.tzinfo)[0]
                        if reminder.recurring_until is None or when_t <= reminder.recurring_until:
                            reminder.at_time = when_t
                        else:
                            reminder.sent_reminder = True
                    else:
                        reminder.sent_reminder = True
                    reminder.save()
            time.sleep(1)

    def handle_new_reminder(self, connection, event, match):
        """Watch IRC for requests to remind new things, create countdown items."""
        if event.in_privmsg or event.addressed:
            log.debug("%s is a new reminder request", most_specific_message(event))
            who = match.group('who')
            when_type = match.group('when_type')
            when = match.group('when')
            recurring_period = match.group('recurring_period')
            recurring_until = match.group('recurring_until')
            text = match.group('text')
            name = match.group('name')
            log.debug("%s / %s / %s", who, when, text)

            if not name:
                item_name = '{0:s}-{1:s}'.format(event.sender_nick, timezone.now().strftime('%s'))
            else:
                if CountdownItem.objects.filter(name=name).count() > 0:
                    self.bot.reply(event, "item with name '{0:s}' already exists".format(name))
                    return 'NO MORE'
                item_name = name

            # parse when to send the notification
            if when_type == 'in':
                # relative time
                calendar = pdt.Calendar()
                when_t = calendar.parseDT(when, timezone.localtime(timezone.now()),
                                          tzinfo=timezone.get_current_timezone())[0]
            else:
                # absolute time
                when_t = timezone.make_aware(parse(when))

            # parse the person to address, if anyone, when sending the notification
            if who == 'us' or who == event.sent_location or event.in_privmsg:
                message = text
            elif who == 'me':
                message = '{0:s}: {1:s}'.format(event.sender_nick, text)
            else:
                message = '{0:s}: {1:s}'.format(who, text)

            # replace pronouns and stuff
            if who == 'me':
                message = re.sub(r'\bI\b', r'you', message, flags=re.IGNORECASE)
                message = re.sub(r'\bme\b', r'you', message, flags=re.IGNORECASE)
                message = re.sub(r'\bmy\b', r'your', message, flags=re.IGNORECASE)
                message = re.sub(r'\bmyself\b', r'yourself', message, flags=re.IGNORECASE)
            elif who == 'us':
                message = re.sub(r'\bwe\b', r'you', message, flags=re.IGNORECASE)
                message = re.sub(r'\bus\b', r'you', message, flags=re.IGNORECASE)
                message = re.sub(r'\bour\b', r'your', message, flags=re.IGNORECASE)
                message = re.sub(r'\bourselves\b', r'yourselves', message, flags=re.IGNORECASE)

            log.debug("%s / %s / %s", item_name, when_t, message)

            # get the IrcChannel to send to
            reminder_target, _ = IrcChannel.objects.get_or_create(name=event.sent_location,
                                                                  server=connection.server_config)

            countdown_item = CountdownItem.objects.create(name=item_name, at_time=when_t, is_reminder=True,
                                                          reminder_message=message, reminder_target_new=reminder_target)
            if recurring_period:
                countdown_item.recurring_period = recurring_period
            if recurring_until:
                recurring_until_t = timezone.make_aware(parse(recurring_until))
                countdown_item.recurring_until = recurring_until_t
            countdown_item.save()
            log.info("created countdown item %s", str(countdown_item))

            if event.in_privmsg:
                self.bot.reply(event, "ok, i'll message you at {0:s} 14[{1:s}]".format(str(when_t),
                                                                                         countdown_item.name))
            else:
                self.bot.reply(event, "ok, i'll message {0:s} at {1:s} 14[{2:s}]".format(event.sent_location,
                                                                                           str(when_t),
                                                                                           countdown_item.name))

            return 'NO MORE'

    def handle_item_detail(self, connection, event, match):
        """Provide the details of one countdown item."""
        name = match.group(1)

        if name != 'list':
            try:
                item = CountdownItem.objects.get(name=name)
                rdelta = relativedelta(item.at_time, timezone.now())
                relstr = "{0:s} will occur in ".format(name)
                if rdelta.years != 0:
                    relstr += "{0:s} year{1:s} ".format(str(rdelta.years), "s" if abs(rdelta.years) != 1 else "")
                if rdelta.months != 0:
                    relstr += "{0:s} month{1:s}, ".format(str(rdelta.months), "s" if abs(rdelta.months) != 1 else "")
                if rdelta.days != 0:
                    relstr += "{0:s} day{1:s}, ".format(str(rdelta.days), "s" if abs(rdelta.days) != 1 else "")
                if rdelta.hours != 0:
                    relstr += "{0:s} hour{1:s}, ".format(str(rdelta.hours), "s" if abs(rdelta.hours) != 1 else "")
                if rdelta.minutes != 0:
                    relstr += "{0:s} minute{1:s}, ".format(str(rdelta.minutes), "s" if abs(rdelta.minutes) != 1 else "")
                if rdelta.seconds != 0:
                    relstr += "{0:s} second{1:s}, ".format(str(rdelta.seconds), "s" if abs(rdelta.seconds) != 1 else "")
                # remove trailing comma from output
                reply = relstr[0:-2]
                return self.bot.reply(event, reply)
            except CountdownItem.DoesNotExist:
                return self.bot.reply(event, "countdown item '{0:s}' not found".format(name))

    def handle_item_list(self, connection, event, match):
        """List all countdown items."""
        items = CountdownItem.objects.all()
        if len(items) > 0:
            reply = "countdown items: {0:s}".format(", ".join([x.name for x in items]))
            return self.bot.reply(event, reply)
        else:
            return self.bot.reply(event, "no countdown items are configured")


plugin = Countdown