import datetime

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update, Message

import utils
import worker
import job_storage
from termin_api import Buro


def get_msg(update: Update) -> Message:
    """
    Gets `Message` object which can be used for answers.
    It is needed because way to get Message is different for callback updates and normal updates
    :param update: Update
    :return: Message
    """
    if update.message:
        return update.message
    else:
        return update.callback_query.message


def print_subscription_status(update, context):
    msg = get_msg(update)

    chat_id = str(update.effective_chat.id)
    subscription = job_storage.get_job(chat_id)

    if subscription:
        subscription_limit = subscription.kwargs['created_at'] + datetime.timedelta(days=7)
        date_object = subscription_limit.strftime("%d-%m-%Y %H:%M:%S")

        department = Buro.get_buro_by_id(subscription.kwargs['buro'])
        msg.reply_text(
            'Current subscription details:\n\n - Department: %s \n - Type: %s \n - Interval: %s \n - Until: %s \n' % (
                department.get_name(), subscription.kwargs['termin'], subscription.trigger.interval, date_object))
        print_unsubscribe_button(chat_id)


def notify_about_termins(chat_id, buro, termin, created_at):
    """
    Checks for available termins and prints them if any
    """

    department = Buro.get_buro_by_id(buro)

    if department is None:
        return

    bot = utils.get_bot()

    appointments = worker.get_available_appointments(department, termin, user_id=str(chat_id))

    if appointments is None:
        bot.send_message(chat_id=chat_id,
                         text=f'Seems like appointment title <{termin}> is not accepted by the buro <%s> any more\n'
                              'Please check of issues on Github and create one if not reported yet '
                              '(https://github.com/okainov/munich-scripts/issues/new)\n'
                              'In the meantime, we\'ve removed this subscription in order to prevent sending '
                              'more of such useless messages :( Please come back later'
                              % department.get_name())
        job_storage.remove_subscription(chat_id)

    if len(appointments) > 0:
        for caption, date, time in appointments:
            bot.send_message(chat_id=chat_id, text='The nearest appointments at %s are on %s:\n%s' % (
                caption, date, '\n'.join(time)))
        bot.send_message(chat_id=chat_id, text='Please book your appointment here: %s' % department.get_frame_url())
        print_unsubscribe_button(chat_id)


def print_subscription_status_for_termin(update, context):
    msg = get_msg(update)

    chat_id = str(update.effective_chat.id)
    termin = context.user_data['termin_type']
    subscription = job_storage.get_job(chat_id)

    if subscription and subscription.kwargs['termin'] == termin:
        subscription_limit = subscription.kwargs['created_at'] + datetime.timedelta(days=7)
        date_object = subscription_limit.strftime("%d-%m-%Y")

        msg.reply_text(
            'Subscription with interval %sm is already active until %s \n' % (
                subscription.trigger.interval, date_object))
        print_unsubscribe_button(chat_id)
    else:
        buttons = [InlineKeyboardButton(text="Subscribe", callback_data="subscribe")]
        custom_keyboard = [buttons]
        msg.reply_text(
            'If you want, you can subscribe for available appointments of this type. '
            'After then you will receive regular updates about available appointments for a week',
            reply_markup=InlineKeyboardMarkup(custom_keyboard, one_time_keyboard=True))


def print_main_message(update, context):
    deps = Buro.__subclasses__()

    MAX_BURO_IN_ROW = 2
    custom_keyboard = []
    buttons = []
    for i, dep in enumerate(deps):
        buttons.append(InlineKeyboardButton(text=dep.get_name(), callback_data=dep.__name__))
        if i % MAX_BURO_IN_ROW == MAX_BURO_IN_ROW - 1:
            custom_keyboard.append(buttons)
            buttons = []
    if buttons:
        custom_keyboard.append(buttons)

    if 'buro' in context.user_data and 'termin_type' in context.user_data:
        custom_keyboard.append([InlineKeyboardButton(text='Reuse last selection', callback_data='_REUSE')])

    msg = get_msg(update)

    msg.reply_text(
        'Here are available departments. Please select one:',
        reply_markup=InlineKeyboardMarkup(custom_keyboard, one_time_keyboard=True))

    print_subscription_status(update, context)


def print_stat_message(update, context):
    chat_id = update.effective_chat.id
    utils.get_logger().info(f'[{chat_id}] Displaying statistics', extra={'user': chat_id})

    msg = get_msg(update)
    all_jobs = [x for x in job_storage.get_jobs() if 'chat_id' in x.kwargs]
    average_interval = sum([x.trigger.interval.seconds for x in all_jobs]) / 60 / len(all_jobs)

    termin_list = [x.kwargs['termin'] for x in all_jobs]
    most_popular_termin = max(set(termin_list), key=lambda x: termin_list.count(x))

    msg.reply_text(f'ℹ️ Some piece of statistics:\n\n'
                   f'{len(all_jobs)} active subscription(s)\n'
                   f'{average_interval} min average interval\n'
                   f'{most_popular_termin} is the most popular termin')


def print_termin_type_message(update, context):
    buttons = []
    msg = get_msg(update)

    department = context.user_data['buro']

    msg.reply_text(
        'Fetching available appointment types...')
    for i, x in department.get_typical_appointments():
        buttons.append([InlineKeyboardButton(text=x, callback_data=i)])
    if department.get_typical_appointments():
        buttons.append([InlineKeyboardButton(text='--------------', callback_data='-1')])

    for i, x in enumerate(department.get_available_appointment_types()):
        buttons.append([InlineKeyboardButton(text=x, callback_data=i)])

    msg.reply_text(
        'There are several appointment types available. Most used types are on top. Please select one',
        reply_markup=InlineKeyboardMarkup(buttons, one_time_keyboard=True))


def print_quering_message(update, context):
    msg = get_msg(update)

    termin_type_str = context.user_data['termin_type']

    msg.reply_text(
        'Great, wait a second while I\'m fetching available appointments for %s...' % termin_type_str)

    print_available_termins(update, context, print_if_none=True)

    buttons = [InlineKeyboardButton(text="Return back", callback_data="return")]
    custom_keyboard = [buttons]

    msg.reply_text(
        text='Or just return to the main selection',
        reply_markup=InlineKeyboardMarkup(custom_keyboard, one_time_keyboard=True))


def print_available_termins(update, context, print_if_none=False):
    """
    Checks for available termins and prints them if any
    """
    department = context.user_data['buro']
    termin_type_str = context.user_data['termin_type']

    msg = get_msg(update)

    appointments = worker.get_available_appointments(department, termin_type_str, user_id=str(update.effective_chat.id))

    if appointments is None:
        msg.reply_text(
            'Seems like appointment title <%s> is not accepted by the buro <%s> any more\n'
            'Possible reasons:\n'
            '- The buro has updated their appointments page and bot requires a fix\n'
            '- Our data about this appointment is out-of-date\n'
            'Please check for issues on Github and create a new one if needed'
            ' (https://github.com/okainov/munich-scripts/issues/new)' % (
                termin_type_str, department))

    if len(appointments) > 0:
        for caption, date, time in appointments:
            msg.reply_text('The nearest appointments at %s are on %s:\n%s' % (
                caption, date, '\n'.join(time)))
        msg.reply_text('Please book your appointment here: %s' % department.get_frame_url())
        print_subscription_status_for_termin(update, context)
    elif print_if_none:
        msg.reply_text('Unfortunately, everything is booked. Please come back in several days :(')
        print_subscription_status_for_termin(update, context)


def print_unsubscribe_button(chat_id):
    buttons = [InlineKeyboardButton(text="Unsubscribe", callback_data="_STOP")]
    custom_keyboard = [buttons]
    utils.get_bot().send_message(chat_id,
                                 'To unsubscribe click the button',
                                 reply_markup=InlineKeyboardMarkup(custom_keyboard, one_time_keyboard=True))


def print_subscribe_message(update, context):
    msg = get_msg(update)
    MIN_CHECK_INTERVAL_MINUTES = utils.get_min_interval()
    msg.reply_text(
        f'Please type interval in minutes. Interval should greater or equal than {MIN_CHECK_INTERVAL_MINUTES} minutes.')