"""Monitor and playback IRC stuff."""
import logging
from datetime import datetime

import irc.client

from ircbot.lib import Plugin, most_specific_message

logger = logging.getLogger(__name__)


class History(Plugin):
    """Watch the history of IRC channels and try to track what users may have missed."""

    what_missed_regex = r'(?i)(what did I miss\?|did I miss anything\?)$'

    def __init__(self, bot, connection, event):
        """Initialize some tracking stuff."""
        super(History, self).__init__(bot, connection, event)
        self.channel_history = {}
        self.channel_participants = {}
        self.channel_leave_points = {}

    def start(self):
        """Set up the handlers."""
        logger.debug("%s starting up", __name__)
        self.connection.add_global_handler('pubmsg', self.handle_chatter, 50)
        self.connection.add_global_handler('join', self.handle_join, 50)
        self.connection.add_global_handler('part', self.handle_part, 50)
        self.connection.add_global_handler('quit', self.handle_quit, 50)

        self.connection.reactor.add_global_regex_handler(['pubmsg', 'privmsg'], self.what_missed_regex,
                                                         self.handle_what_missed, 60)

        super(History, self).start()

    def stop(self):
        """Tear down handlers."""
        logger.debug("%s shutting down", __name__)
        self.connection.remove_global_handler('pubmsg', self.handle_chatter)
        self.connection.remove_global_handler('join', self.handle_join)
        self.connection.remove_global_handler('part', self.handle_part)
        self.connection.remove_global_handler('quit', self.handle_quit)

        self.connection.reactor.remove_global_regex_handler(['pubmsg', 'privmsg'], self.handle_what_missed)

        super(History, self).stop()

    def handle_chatter(self, connection, event):
        """Track IRC chatter."""
        what = event.arguments[0]
        where = event.target
        who = irc.client.NickMask(event.source).nick
        when = datetime.now()

        logger.debug("tracking message for %s: (%s,%s)", where, who, what)
        history = self.channel_history.setdefault(where, [])
        history.append((where, when.isoformat(), who, what))
        logger.debug("history for %s: %s", where, history)

        # for when we maybe don't see a join, if they talked in the channel, add them to it
        self._add_channel_participant(where, who)

    def handle_join(self, connection, event):
        """Track who is entitled to see channel history."""
        where = event.target
        who = irc.client.NickMask(event.source).nick
        logger.debug("%s joined %s", who, where)

        self._add_channel_participant(where, who)

    def handle_part(self, connection, event):
        """Note when people leave IRC channels."""
        where = event.target
        who = irc.client.NickMask(event.source).nick
        logger.debug("%s left %s", who, where)

        # if they parted the channel, they must have been in it, so note their point in history
        self._add_channel_leave_point(where, who)
        self._remove_channel_participant(where, who)

    def handle_quit(self, connection, event):
        """Note when people leave IRC."""
        who = irc.client.NickMask(event.source).nick
        logger.debug("%s disconnected", who)

        # find all channels the quitter was in, save their leave points
        for channel in self.channel_participants.keys():
            self._add_channel_leave_point(channel, who)
            self._remove_channel_participant(channel, who)

    def handle_what_missed(self, connection, event, match):
        """Tell the user what they missed."""
        who = irc.client.NickMask(event.source).nick
        if event.in_privmsg or event.addressed:
            logger.debug("<%s> %s is asking for an update", who, most_specific_message(event))
            if event.in_privmsg:
                total_history = []
                channel_count = 0
                for channel in self.channel_leave_points.keys():
                    logger.debug("checking history slice for %s", channel)
                    total_history += self._missed_slice(channel, who)
                    self._delete_channel_leave_point(channel, who)
                    logger.debug("total history so far: %s", total_history)
                    channel_count += 1
                logger.debug("final missed history: %s", total_history)
                self._send_history(who, total_history)
                self.bot.reply(event, f"{len(total_history)} line(s) over {channel_count} channel(s)")
                return 'NO MORE'
            else:
                where = event.target
                history = self._missed_slice(where, who)
                self._delete_channel_leave_point(where, who)
                self._send_history(who, history)
                privmsged_str = " (PRIVMSGed)" if history else ""
                self.bot.reply(event, f"{len(history)} line(s){privmsged_str}")
                return 'NO MORE'

    def _send_history(self, who, history):
        """Reply to who with missed history."""
        for line in history:
            self.bot.privmsg(who, f"{line[0]}: [{line[1]}] <{line[2]}> {line[3]}")

    def _add_channel_leave_point(self, where, who):
        """Note that the given who left the channel at the current history point."""
        leave_points = self.channel_leave_points.setdefault(where, {})
        leave_points[who] = len(self.channel_history.setdefault(where, [])) - 1
        logger.debug("leave points for %s: %s", where, leave_points)

    def _delete_channel_leave_point(self, where, who):
        """Remove tracking for user's history point."""
        leave_points = self.channel_leave_points.setdefault(where, {})
        leave_points.pop(who, None)
        logger.debug("leave points for %s: %s", where, leave_points)

    def _add_channel_participant(self, where, who):
        """Add a who to the list of people who are/were in a channel."""
        participants = self.channel_participants.setdefault(where, set())
        participants.add(who)
        logger.debug("participants for %s: %s", where, participants)

    def _missed_slice(self, where, who):
        """Get the lines in where since who last left."""
        leave_points = self.channel_leave_points.setdefault(where, {})
        if leave_points.get(who) is not None:
            leave_point = leave_points.get(who) + 1
            history = self.channel_history.setdefault(where, [])
            missed_history = history[leave_point:]
            logger.debug("in %s, %s missed: %s", where, who, missed_history)
            return missed_history
        return []

    def _remove_channel_participant(self, where, who):
        """Remove the specified who from the where channel's participants list."""
        participants = self.channel_participants.setdefault(where, set())
        participants.discard(who)
        logger.debug("participants for %s: %s", where, participants)


plugin = History