"""Karma hooks for the IRC bot."""

import logging
import re

import irc.client

from django.conf import settings
from django.db.models import Count, Sum

from ircbot.lib import Plugin
from karma.models import KarmaKey, KarmaLogEntry

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


class Karma(Plugin):

    """Track karma and report on it."""

    def start(self):
        """Set up the handlers."""

        self.connection.add_global_handler('pubmsg', self.handle_chatter, -20)
        self.connection.add_global_handler('privmsg', self.handle_chatter, -20)

        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
                                                         (r'^!karma\s+keyreport\s+(.*)'),
                                                         self.handle_keyreport, -20)
        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
                                                         r'^!karma\s+rank\s+(.*)$',
                                                         self.handle_rank, -20)
        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
                                                         (r'^!karma\s+report\s+(highest|lowest|positive|negative'
                                                          r'|top|opinionated)'),
                                                         self.handle_report, -20)
        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'],
                                                         r'^!karma\s+stats\s+([\S]+)',
                                                         self.handle_stats, -20)

        super(Karma, self).start()

    def stop(self):
        """Tear down handlers."""

        self.connection.remove_global_handler('pubmsg', self.handle_chatter)
        self.connection.remove_global_handler('privmsg', self.handle_chatter)

        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_keyreport)
        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_rank)
        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_report)
        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_stats)

        super(Karma, self).stop()

    @staticmethod
    def handle_chatter(connection, event):
        """Watch karma from IRC chatter."""

        what = event.arguments[0].lower()
        karma_pattern = r'(?:\((.+?)\)|(\S+))(\+\+|--|\+-|-\+)(\s+|$)'

        where = event.target
        if where in settings.KARMA_IGNORE_CHATTER_TARGETS:
            log.debug("ignoring chatter in {0:s}".format(where))
            return

        # check the line for karma
        log.debug("searching '%s' for karma", what)
        matches = re.findall(karma_pattern, what, re.IGNORECASE)
        for match in matches:
            key = match[0] if match[0] else match[1]
            value = match[2]

            log.debug("key length: %d", len(key))
            if len(key) > KarmaKey._meta.get_field('key').max_length:
                log.warning("given a key longer than %d, ignoring", KarmaKey._meta.get_field('key').max_length)
                return

            karma_key, c = KarmaKey.objects.get_or_create(key=key)

            if value == '++':
                KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)
            elif value == '--':
                KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
            elif value == '+-':
                KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)
                KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
            elif value == '-+':
                KarmaLogEntry.objects.create(key=karma_key, delta=-1, nickmask=event.source)
                KarmaLogEntry.objects.create(key=karma_key, delta=1, nickmask=event.source)

    def handle_rank(self, connection, event, match):
        """Report on the rank of a karma item."""

        where = event.target
        if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
            log.debug("ignoring command in {0:s}".format(where))
            return

        key = match.group(1).lower().rstrip()
        try:
            karma_key = KarmaKey.objects.get(key=key)
            return self.bot.reply(event, "{0:s} has {1:d} points of karma (rank {2:d})".format(karma_key.key,
                                                                                               karma_key.score(),
                                                                                               karma_key.rank()))
        except KarmaKey.DoesNotExist:
            return self.bot.reply(event, "i have not seen any karma for {0:s}".format(match.group(1)))

    def handle_keyreport(self, connection, event, match):
        """Provide report on a karma key."""
        key = match.group(1).lower().rstrip()
        try:
            karma_key = KarmaKey.objects.get(key=key)
            karmaers = KarmaLogEntry.objects.filter(key=karma_key)
            karmaers = karmaers.values('nickmask').annotate(Sum('delta')).annotate(Count('delta')).order_by('-delta__count')
            karmaers_list =  [f"{irc.client.NickMask(x['nickmask']).nick} ({x['delta__count']}, {'+' if x['delta__sum'] >= 0 else ''}{x['delta__sum']})" for x in karmaers]
            karmaers_list_str = ", ".join(karmaers_list[:10])
            return self.bot.reply(event, f"most opinionated on {key}: {karmaers_list_str}")
        except KarmaKey.DoesNotExist:
            return self.bot.reply(event, "i have not seen any karma for {0:s}".format(match.group(1)))

    def handle_report(self, connection, event, match):
        """Provide some karma reports."""

        where = event.target
        if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
            log.debug("ignoring command in {0:s}".format(where))
            return

        report = match.group(1).lower()
        if report == 'highest':
            sorted_keys = KarmaKey.objects.ranked_scored_order()
            msg = "top 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]]))
            return self.bot.reply(event, msg)
        elif report == 'lowest':
            sorted_keys = KarmaKey.objects.ranked_scored_reverse_order()
            msg = "bottom 5 recipients: {0:s}".format(", ".join([str(x) for x in sorted_keys[:5]]))
            return self.bot.reply(event, msg)
        elif report == 'positive':
            karmaers = KarmaLogEntry.objects.optimists()
            karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
                                                             x['karma_outlook']) for x in karmaers[:5]])
            msg = "top 5 optimists: {0:s}".format(karmaer_list)
            return self.bot.reply(event, msg)
        elif report == 'negative':
            karmaers = KarmaLogEntry.objects.pessimists()
            karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
                                                             x['karma_outlook']) for x in karmaers[:5]])
            msg = "top 5 pessimists: {0:s}".format(karmaer_list)
            return self.bot.reply(event, msg)
        elif report == 'top' or report == 'opinionated':
            karmaers = KarmaLogEntry.objects.most_opinionated()
            karmaer_list = ", ".join(["{0:s} ({1:d})".format(irc.client.NickMask(x['nickmask']).nick,
                                                             x['karma_count']) for x in karmaers[:5]])
            msg = "top 5 opinionated users: {0:s}".format(karmaer_list)
            return self.bot.reply(event, msg)

    def handle_stats(self, connection, event, match):
        """Provide stats on a karma user."""

        where = event.target
        if where in settings.KARMA_IGNORE_COMMAND_TARGETS:
            log.debug("ignoring command in {0:s}".format(where))
            return

        karmaer = match.group(1)
        log_entries = KarmaLogEntry.objects.filter(nickmask=karmaer)
        if len(log_entries) == 0:
            # fallback, try to match the nick part of nickmask
            log_entries = KarmaLogEntry.objects.filter(nickmask__startswith='{0:s}!'.format(karmaer))

        if len(log_entries) == 0:
            return self.bot.reply(event, "karma user {0:s} not found".format(karmaer))

        total_karma = log_entries.count()
        positive_karma = log_entries.filter(delta__gt=0).count()
        negative_karma = log_entries.filter(delta__lt=0).count()
        msg = ("{0:s} has given {1:d} postive karma and {2:d} negative karma, for a total of {3:d} karma"
               "".format(karmaer, positive_karma, negative_karma, total_karma))
        return self.bot.reply(event, msg)


plugin = Karma