# -*- coding: utf-8 -*-
import asyncio
import datetime

from irc3.plugins.command import command
from irc3.utils import IrcString
from irc3 import event
import irc3

MOTION_RESULT_LIST = 'Ayes: {ayes}; Nays: {nays}; Abstains: {abstains}'
MOTION_RESULT_COUNT = 'Ayes: {ayes}; Nays: {nays}; Abstains: {abstains}; TOTAL: {total}'
MOTION_EXTERNAL_VOTES = '[+] External ayes: {ayes}; External nays: {nays}'

MOTION_LAPSES_QUORUM = '*** Result: Motion lapses. Quorum of {quorum} not met.'
MOTION_LAPSES_PC = '*** Result: Motion lapses. {in_favour:.2f}% in favour.'
MOTION_CARRIES = '*** Result: Motion carries. {in_favour:.2f}% in favour.'


@irc3.plugin
class Motions(object):

    def __init__(self, bot):
        bot.include('casemapping')
        bot.include('mappinguserlist')
        bot.include('irc3.plugins.async')
        bot.include('irc3.plugins.core')
        self.bot = bot
        self.name = bot.config.get('name', 'motionbot')
        self.states = {}
        self.db = None

        # setup database if we're using one
        db_uri = self.bot.config.get('database', None)
        if db_uri:
            from pymongo import MongoClient
            self.db_client = MongoClient(db_uri)
            self.db = self.db_client.motionbot
            print('Using database')
        else:
            print('Not using database')

    # channel permissions
    def is_voice(self, mask, target):
        target = self.bot.casefold(target)
        if not isinstance(mask, IrcString):
            mask = IrcString(mask)
        return mask.nick in self.bot.channels[target].modes['+']

    def is_admin(self, mask, target):
        target = self.bot.casefold(target)
        if not isinstance(mask, IrcString):
            mask = IrcString(mask)

        # we consider halfop/op and above admins
        prefixes = self.bot.config['server_config'].get('PREFIX', '(ov)@+')
        prefixes = prefixes.split(')')[1]
        admin_prefix_index = prefixes.index('%') if '%' in prefixes else prefixes.index('@')
        admin_prefixes = prefixes[:admin_prefix_index + 1]

        for prefix in admin_prefixes:
            if mask.nick in self.bot.channels[target].modes[prefix]:
                return True

        return False

    # channel info init
    @event(irc3.rfc.JOIN)
    def join_chan(self, mask=None, channel=None):
        """Upon joining a channel, keep track of the channel state."""
        channel = self.bot.casefold(channel)
        if mask.nick == self.bot.nick:
            self.states[channel] = {
                'recognised': [],
                'meeting': {
                    'name': '',
                    'started': False,
                    'quorum': 0,
                },
                'motion': {
                    'text': '',
                    'put_by': '',
                    'started': False,
                    'votes': {},
                },
            }
        else:
            userhost = mask.split('!', 1)[1]
            if userhost in self.states[channel]['recognised']:
                nick = self.bot.casefold(mask.nick)
                self.bot.mode(channel, '+v {}'.format(nick))

            if self.db:
                doc = self.db.recognised.find_one({
                    'bot': self.name,
                    'channel': channel,
                })

                if doc:
                    self.states[channel]['recognised'] = doc['users']
                else:
                    self.db.recognised.insert_one({
                        'bot': self.name,
                        'channel': channel,
                        'users': [],
                    })

    # op commands
    @command()
    @asyncio.coroutine
    def add(self, mask, target, args):
        """Recognise a user.

        %%add <nick>
        """
        # we only care about ops and commands to channels
        if not (target.is_channel and self.is_admin(mask, target)):
            return

        channel = self.bot.casefold(target)

        # voice user
        nick = args['<nick>'].lower()
        if not (self.is_admin(nick, channel) or self.is_voice(nick, channel)):
            if self.is_admin(self.bot.nick, channel):
                self.bot.mode(channel, '+v {}'.format(nick))
            else:
                self.bot.notice(channel, '*** I am not opped and cannot voice user.')

        # add user to our recognised list
        info = yield from self.bot.async.whois(nick=nick)

        if not info['success']:
            self.bot.notice(channel, '*** Could not add user to recognised list.')
            return

        userhost = '{username}@{host}'.format(**info)
        if userhost not in self.states[channel]['recognised']:
            self.states[channel]['recognised'].append(userhost)

            if self.db:
                self.db.recognised.update_one({
                        'bot': self.name,
                        'channel': channel,
                    }, {
                        '$push': {
                            'users': userhost,
                        }
                    }
                )

    @command()
    def quorum(self, mask, target, args):
        """Set or see the quorum for the current meeting.

        %%quorum [<number>]
        """
        # we only care about ops and commands to channels
        if not target.is_channel:
            return

        number = args['<number>']
        target = self.bot.casefold(target)

        if number:
            if self.is_admin(mask, target):
                try:
                    number = int(number)
                except ValueError:
                    self.bot.notice(target, '*** Quorum must be an integer')
                    return

                self.states[target]['meeting']['quorum'] = number
                self.bot.notice(target, '*** Quorum now set to: {}'.format(number))

        else:
            current_number = self.states[target]['meeting']['quorum']
            self.bot.notice(target, '*** Quorum is: {}'.format(current_number))

    @command()
    def meeting(self, mask, target, args):
        """Set or see the name for the current meeting.

        %%meeting [<name>...]
        """
        # we only care about ops and commands to channels
        if not target.is_channel:
            return