import emoji
import logging
import random
import re
from telegram import InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ConversationHandler

import captions
import helpers
import mdformat
import settings
import util
from components import botlistchat
from const import CallbackActions, CallbackStates
from dialog import messages
from lib import InlineCallbackButton
from models import Bot, Category, Favorite, Keyword, Statistic, User, track_activity
from util import track_groups
from typing import *

log = logging.getLogger()


def show_official(bot, update):
    text = "*Official Telegram Bots:*\n\n"
    update.effective_message.reply_text(
        text + Bot.get_official_bots_markdown(), parse_mode="markdown"
    )


@track_activity("explore", "bots", Statistic.ANALYSIS)
def explore(bot, update, chat_data):
    cid = update.effective_chat.id
    uid = update.effective_user.id
    mid = util.mid_from_update(update)
    explorable_bots = Bot.explorable_bots()

    chat_data["explored"] = chat_data.get("explored", list())

    # don't explore twice
    for explored in chat_data["explored"]:
        explorable_bots.remove(explored)

    if len(explorable_bots) == 0:
        util.send_md_message(
            bot,
            cid,
            mdformat.none_action(
                "You have explored all the bots. Congratulations, you might be the first šŸ˜œ"
            ),
        )
        return

    random_bot = random.choice(explorable_bots)

    buttons = [
        [
            InlineKeyboardButton(
                captions.ADD_TO_FAVORITES,
                callback_data=util.callback_for_action(
                    CallbackActions.ADD_TO_FAVORITES, {"id": random_bot.id}
                ),
            ),
            InlineKeyboardButton(
                captions.SHARE, switch_inline_query=random_bot.username
            ),
        ],
        [
            InlineKeyboardButton(
                random_explore_text(),
                callback_data=util.callback_for_action(CallbackActions.EXPLORE_NEXT),
            )
        ],
    ]

    markup = InlineKeyboardMarkup(buttons)

    text = random_bot.detail_text

    if uid in settings.MODERATORS and util.is_private_message(update):
        text += "\n\nšŸ›ƒ /edit{}".format(random_bot.id)

    msg = bot.formatter.send_or_edit(cid, text, to_edit=mid, reply_markup=markup)
    chat_data["explored"].append(random_bot)

    # import time
    # time.sleep(2)
    # msg.edit_reply_markup(reply_markup=ForceReply(selective=True))


def random_explore_text():
    choices = [
        "Explore",
        "Get me another",
        "Next",
        "Another one",
        "More",
        "One more",
        "Next one",
        "Hit me",
    ]
    return "{} šŸ”„".format(random.choice(choices))


def _select_category_buttons(callback_action=None):
    if callback_action is None:
        # set default
        callback_action = CallbackActions.SELECT_BOT_FROM_CATEGORY
    categories = Category.select().order_by(Category.name.asc()).execute()

    buttons = util.build_menu(
        [
            InlineKeyboardButton(
                "{}{}".format(emoji.emojize(c.emojis, use_aliases=True), c.name),
                callback_data=util.callback_for_action(callback_action, {"id": c.id}),
            )
            for c in categories
        ],
        2,
    )
    buttons.insert(
        0,
        [
            InlineKeyboardButton(
                "šŸ†• New Bots",
                callback_data=util.callback_for_action(
                    CallbackActions.NEW_BOTS_SELECTED
                ),
            )
        ],
    )
    return buttons


@track_activity("menu", "select category", Statistic.ANALYSIS)
@track_groups
def select_category(bot, update, chat_data, callback_action=None):
    chat_id = update.effective_chat.id
    reply_markup = InlineKeyboardMarkup(_select_category_buttons(callback_action))
    reply_markup, callback = botlistchat.append_restricted_delete_button(
        update, chat_data, reply_markup
    )
    msg = bot.formatter.send_or_edit(
        chat_id,
        util.action_hint(messages.SELECT_CATEGORY),
        to_edit=util.mid_from_update(update),
        reply_markup=reply_markup,
    )
    callback(msg)
    return ConversationHandler.END


@track_activity("explore", "new bots", Statistic.ANALYSIS)
def show_new_bots(bot, update, chat_data, back_button=False):
    chat_id = update.effective_chat.id
    channel = helpers.get_channel()

    buttons = []

    if channel:
        buttons.append(
            [
                InlineKeyboardButton(
                    "Show in BotList",
                    url="http://t.me/{}/{}".format(
                        channel.username, channel.new_bots_mid
                    ),
                )
            ]
        )

    buttons.append(
        [
            InlineKeyboardButton(
                "Share", switch_inline_query=messages.NEW_BOTS_INLINEQUERY
            )
        ]
    )

    if back_button:
        buttons[0].insert(
            0,
            InlineKeyboardButton(
                captions.BACK,
                callback_data=util.callback_for_action(CallbackActions.SELECT_CATEGORY),
            ),
        )
    reply_markup = InlineKeyboardMarkup(buttons)
    reply_markup, callback = botlistchat.append_restricted_delete_button(
        update, chat_data, reply_markup
    )
    msg = bot.formatter.send_or_edit(
        chat_id,
        _new_bots_text(),
        to_edit=util.mid_from_update(update),
        reply_markup=reply_markup,
        reply_to_message_id=util.mid_from_update(update),
    )
    callback(msg)
    return ConversationHandler.END


def send_category(bot, update, chat_data, category):
    uid = util.uid_from_update(update)
    cid = update.effective_chat.id
    bots = Bot.of_category_without_new(category)[: settings.MAX_BOTS_PER_MESSAGE]
    bots_with_description = [b for b in bots if b.description is not None]
    detailed_buttons_enabled = len(
        bots_with_description
    ) > 0 and util.is_private_message(update)

    callback = CallbackActions.SEND_BOT_DETAILS

    if detailed_buttons_enabled:
        buttons = [
            InlineKeyboardButton(
                x.username,
                callback_data=util.callback_for_action(callback, {"id": x.id}),
            )
            for x in bots_with_description
        ]
    else:
        buttons = []
    menu = util.build_menu(buttons, 2)
    menu.insert(
        0,
        [
            InlineKeyboardButton(
                captions.BACK,
                callback_data=util.callback_for_action(CallbackActions.SELECT_CATEGORY),
            ),
            InlineKeyboardButton(
                "Show in BotList",
                url="http://t.me/botlist/{}".format(category.current_message_id),
            ),
            InlineKeyboardButton("Share", switch_inline_query=category.name),
        ],
    )
    txt = "There are *{}* bots in the category *{}*:\n\n".format(
        len(bots), str(category)
    )

    if uid in settings.MODERATORS and util.is_private_message(update):
        # append admin edit buttons
        txt += "\n".join(["{} ā€” /edit{} šŸ›ƒ".format(b, b.id) for b in bots])
    else:
        txt += "\n".join([str(b) for b in bots])

    if detailed_buttons_enabled:
        txt += "\n\n" + util.action_hint(
            "Press a button below to get a detailed description."
        )

    reply_markup = InlineKeyboardMarkup(menu)
    reply_markup, callback = botlistchat.append_restricted_delete_button(
        update, chat_data, reply_markup
    )
    msg = bot.formatter.send_or_edit(
        cid, txt, to_edit=util.mid_from_update(update), reply_markup=reply_markup
    )
    callback(msg)
    Statistic.of(
        update, "menu", "of category {}".format(str(category)), Statistic.ANALYSIS
    )


def send_bot_details(
    bot, update, chat_data, item=None, header_msg: Optional[str] = None
):
    is_group = util.is_group_message(update)
    cid = update.effective_chat.id
    user = User.from_update(update)

    if item is None:
        if is_group:
            return

        try:
            text = update.message.text
            bot_in_text = re.findall(settings.REGEX_BOT_IN_TEXT, text)[0]
            item = Bot.by_username(bot_in_text, include_disabled=True)

        except Bot.DoesNotExist:
            update.message.reply_text(
                util.failure(
                    "This bot is not in the @BotList. If you think this is a "
                    "mistake, see the /examples for /contributing."
                )
            )
            return

    header_buttons = []
    buttons = []
    if item.disabled:
        txt = "{} {} and thus removed from the @BotList.".format(
            item, Bot.DisabledReason.to_str(item.disabled_reason)
        )
    elif item.approved:
        # bot is already in the botlist => show information
        txt = f"{header_msg}\n\n{item.detail_text}" if header_msg else item.detail_text

        if (
            item.description is None
            and not Keyword.select().where(Keyword.entity == item).exists()
        ):
            txt += " is in the @BotList, but has no description or keywords yet."
        btn = InlineCallbackButton(
            captions.BACK_TO_CATEGORY,
            CallbackActions.SELECT_BOT_FROM_CATEGORY,
            {"id": item.category.id},
        )
        header_buttons.insert(0, btn)
        header_buttons.append(
            InlineKeyboardButton(captions.SHARE, switch_inline_query=item.username)
        )

        # if cid in settings.MODERATORS:
        header_buttons.append(
            InlineKeyboardButton(
                "šŸ“ Edit",
                callback_data=util.callback_for_action(
                    CallbackActions.EDIT_BOT, {"id": item.id}
                ),
            )
        )

        # Add favorite button
        favorite_found = Favorite.search_by_bot(user, item)
        if favorite_found:
            buttons.append(
                InlineKeyboardButton(
                    captions.REMOVE_FAVORITE_VERBOSE,
                    callback_data=util.callback_for_action(
                        CallbackActions.REMOVE_FAVORITE,
                        {"id": favorite_found.id, "details": True},
                    ),
                )
            )
        else:
            buttons.append(
                InlineKeyboardButton(
                    captions.ADD_TO_FAVORITES,
                    callback_data=util.callback_for_action(
                        CallbackActions.ADD_TO_FAVORITES,
                        {"id": item.id, "details": True},
                    ),
                )
            )
    else:
        txt = "{} is currently pending to be accepted for the @BotList.".format(item)
        if cid in settings.MODERATORS:
            header_buttons.append(
                InlineKeyboardButton(
                    "šŸ›ƒ Accept / Reject",
                    callback_data=util.callback_for_action(
                        CallbackActions.APPROVE_REJECT_BOTS, {"id": item.id}
                    ),
                )
            )

    if buttons or header_buttons:
        reply_markup = InlineKeyboardMarkup(
            util.build_menu(buttons, n_cols=3, header_buttons=header_buttons)
        )
    else:
        reply_markup = None

    reply_markup, callback = botlistchat.append_restricted_delete_button(
        update, chat_data, reply_markup
    )

    # Should we ever decide to show thumbnails *shrug*
    # if os.path.exists(item.thumbnail_file):
    #     preview = True
    #     photo = '[\xad]({})'.format('{}/thumbnail/{}.jpeg'.format(
    #         settings.API_URL,
    #         item.username[1:]
    #     ))
    #     log.info(photo)
    #     txt = photo + txt
    # else:
    #     preview = False

    msg = bot.formatter.send_or_edit(
        cid, txt, to_edit=util.mid_from_update(update), reply_markup=reply_markup
    )
    callback(msg)
    Statistic.of(update, "view-details", item.username, Statistic.ANALYSIS)
    return CallbackStates.SHOWING_BOT_DETAILS


def _new_bots_text():
    new_bots = Bot.select_new_bots()
    if len(new_bots) > 0:
        txt = "Fresh new bots since the last update šŸ’™:\n\n{}".format(
            Bot.get_new_bots_markdown()
        )
    else:
        txt = "No new bots available."
    return txt