#!/usr/bin/env python3
from tinytext import tinytext
import regex
import random
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler, InlineQueryHandler, ChosenInlineResultHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, InlineQueryResultArticle, ParseMode, InputTextMessageContent, ParseMode, ReplyKeyboardMarkup, KeyboardButton, ReplyKeyboardRemove
from telegram.error import BadRequest
import logging, coloredlogs
from collections import namedtuple
from schema import Person, Quote, Category, Nickname
from util import logsetup
from uuid import uuid4

LOGLEVEL = logging.DEBUG
FORMAT = "[%(asctime)s] [%(levelname)7s] %(name)7s: %(message)s"
logger = logsetup("microquote.log", FORMAT, LOGLEVEL)

states = {}
State = namedtuple("State", ['user', 'name', 'data'])

updater = Updater(token='414674736:AAESm7QxWxJWBo_AJdfxmitOZd7676qicIA')
dispatcher = updater.dispatcher

QUOTE_RE = regex.compile(r"""^
        (?:
            (?P<naam>.+)
        :\s*)?

        (?P<qtype>["'])
            (?P<quote>.+?)
        (?P=qtype)

        (?:
            \s+[\-\#]\s*
            (?P<naam>.+)
        )?
        """,
        regex.X | regex.V1)

def _u(update):
    if (update.callback_query is not None):
        return update.callback_query.from_user
    return update.message.from_user

def parse_quote(update):
    parsed_quote = QUOTE_RE.match(update.message.text)
    if not parsed_quote:
        update.message.reply_text("Sorry, die quote is onherkenbaar. Voeg hem handmatig toe met /quoteadd.")
        return

    parsed_quote = parsed_quote.groupdict()

    update.message.reply_text("<i>Je citaat is als volgt ontleed:</i>\n\n<b>Naam</b>: %s\n<b>Quote</b>: %s\n" % (parsed_quote["naam"], parsed_quote["quote"]),
            parse_mode=ParseMode.HTML)

    yes = "Dat klopt."
    no = "Klopt dat? Klopt dat?! Denk het niet, Job!"
    markup = get_confirmation_keyboard(yes, no)
    states[_u(update).id] = State(_u(update), "confirm_quote", (yes, no, parsed_quote))

    update.message.reply_text("Klopt dat?", reply_markup=markup, parse_mode=ParseMode.HTML)

def confirm_quote(update):
    state = states.get(_u(update).id, None)

    if (update.message.text == state.data[1]):
        update.message.reply_text("Oh, sorry. Dan moet je hem handmatig toevoegen met /quoteadd.")
        del states[_u(update).id]
        return

    parsed_quote = state.data[2]

    if Nickname.select().where(Nickname.name == parsed_quote['naam']).exists():
        person = Nickname.get(Nickname.name == parsed_quote['naam']).person
        quote = Quote(person=person, content=parsed_quote['quote'])
        try:
            quote.save()
            quote_done(update, quote)
        except:
            existing = Quote.get(content=parsed_quote['quote'])
            update.message.reply_text("Die quote bestaat al als quote %d" % (existing.id))
            send_quote(update, existing)
    else:
        # The name in the quote has no associated person.
        people = [p.nicks[0].name for p in Person.select()]
        if (people):
            update.message.reply_text("Feest. Ik weet alleen niet wie %s is. Is het een van deze lieve mensen?" % (parsed_quote['naam']))
            markup = get_confirmation_keyboard(*people)
            markup.keyboard.append([KeyboardButton("Nee.")])
            update.message.reply_text('\n'.join(people), reply_markup=markup)
            states[_u(update).id] = State(_u(update), "confirm_person", (state.data[2], people))
        else:
            states[_u(update).id] = State(_u(update), "confirm_person", (state.data[2], people))
            confirm_person(update)

def quote_done(update, quote):
    update.message.reply_text("Feest. Je quote is toegevoegd.", reply_markup=ReplyKeyboardRemove())
    send_quote(update, quote)
    del states[_u(update).id]

def confirm_person(update):
    state = states.get(_u(update).id, None)
    parsed_quote, people = state.data

    if update.message.text not in people:
        person = Person()
        person.save()
        nick = Nickname(name=parsed_quote['naam'], person=person)
        nick.save()
        quote = Quote(person=person, content=parsed_quote['quote'])
        quote.save()
        update.message.reply_text("Oké. %s is toegevoegd als een nieuw persoon." % (parsed_quote['naam']))
        quote_done(update, quote)
    else:
        nick = Nickname(name=parsed_quote['naam'])
        person = Nickname.get(Nickname.name == update.message.text).person
        nick.person = person
        nick.save()
        quote = Quote(person=person, content=parsed_quote['quote'])
        quote.save()
        update.message.reply_text("Prima, dan is %s toegevoegd als een nickname van %s." % (parsed_quote['naam'], update.message.text))
        quote_done(update, quote)

def get_quote_message(quote):
    output = ""
    output += "<b>“%s”</b>\n                —%s" % (quote.content, random.choice(quote.person.nicks).name)
    output += "\n\n<i>#%d, %s</i>" % (quote.id, quote.date.strftime("%H:%M %m/%d"))
    return output

def send_quote(update, quote):
    output = get_quote_message(quote)
    update.message.reply_text(output, parse_mode=ParseMode.HTML)

def get_confirmation_keyboard(*options):
    buttons = [[KeyboardButton(option) for option in options]]
    return ReplyKeyboardMarkup(buttons, one_time_keyboard=True)

def handle_message(bot, update):
    status = states.get(_u(update).id, State(None, None, None))
    if (status.name == None):
        parse_quote(update)
    if (status.name == "confirm_quote"):
        confirm_quote(update)
    if (status.name == "confirm_person"):
        confirm_person(update)
    if (status.name == "add_categories"):
        add_categories(update)
    if (status.name == "add_links"):
        add_links(update)

def match_quotes(query=""):
    quotes = list(Quote.select().order_by(Quote.id.desc()).execute())
    if query:
        quotes = [quote for quote in quotes if query.lower() in quote.content.lower()]
    results = [InlineQueryResultArticle(id=quote.id, title=quote.content,
        input_message_content = InputTextMessageContent(message_text=get_quote_message(quote), parse_mode=ParseMode.HTML)) for quote in quotes][:8]
    return results

def select_quote(bot, update):
    print("Quote wordt geselecteerd.")
    query = update.inline_query.query

    try:
        query = int(query)
    except:
        return update.inline_query.answer(match_quotes(query), cache_time=0, switch_pm_text="New quote?", switch_pm_parameter="newquote")

    try:
        quote = Quote.get(Quote.id == int(query))
    except:
        return update.inline_query.answer([], cache_time=0, switch_pm_text="New quote?", switch_pm_parameter="newquote")

    results = [InlineQueryResultArticle(id=quote.id, title=quote.content,
        input_message_content = InputTextMessageContent(message_text=get_quote_message(quote), parse_mode=ParseMode.HTML))]

    update.inline_query.answer(results, cache_time=0, switch_pm_text="New quote?", switch_pm_parameter="newquote")

msg = MessageHandler(Filters.text, handle_message)
dispatcher.add_handler(InlineQueryHandler(select_quote))
dispatcher.add_handler(msg)

def error_callback(bot, up, error):
    logger.error("Error raised: %s (%s)" % (type(error).__name__, error.message))

dispatcher.add_error_handler(error_callback)

updater.start_polling()
