import traceback
import telebot
from telebot import apihelper
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, MessageEntity, Message, CallbackQuery

from beancount_bot import transaction
from beancount_bot.config import get_config, load_config
from beancount_bot.dispatcher import Dispatcher
from beancount_bot.i18n import _
from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session
from beancount_bot.task import load_task, get_task
from beancount_bot.transaction import get_manager
from beancount_bot.util import logger

apihelper.ENABLE_MIDDLEWARE = True

bot = telebot.TeleBot(token=None, parse_mode=None)


@bot.middleware_handler(update_types=['message'])
def session_middleware(bot_instance, message):
    """
    Session middleware
    :param bot_instance:
    :param message:
    :return:
    """
    bot_instance.session = get_session_for(message.from_user.id)


#######
# Authentication #
#######

def check_auth() -> bool:
    """
    Check if you log in
    :return:
    """
    return SESS_AUTH in bot.session and bot.session[SESS_AUTH]


@bot.message_handler(commands=['start'])
def start_handler(message: Message):
    """
    First chat time authentication
    :param message:
    :return:
    """
    auth = get_session(message.from_user.id, SESS_AUTH, False)
    if auth:
        bot.reply_to(message, _("Have been authenticated！"))
        return
    # 要求鉴权
    bot.reply_to(message, _("Welcome to the accounting robot!Please enter the authentication token:"))


def auth_token_handler(message: Message):
    """
    Login token callback
    :param message:
    :return:
    """
    if check_auth():
        return
    # Unconfirmation is considered an authentication token
    auth_token = get_config('bot.auth_token')
    if auth_token == message.text:
        set_session(message.from_user.id, SESS_AUTH, True)
        bot.reply_to(message, _("Authentic success！"))
    else:
        bot.reply_to(message, _("Authentication token error！"))


#######
# instruction #
#######


@bot.message_handler(commands=['reload'])
def reload_handler(message):
    """
    Overload configuration instruction
    :param message:
    :return:
    """
    if not check_auth():
        bot.reply_to(message, _("Please conduct authentication first！"))
        return
    load_config()
    load_task()
    bot.reply_to(message, _("Successful overload configuration！"))


@bot.message_handler(commands=['help'])
def help_handler(message):
    """
    Help instruction
    :param message:
    :return:
    """
    cmd = message.text
    dispatchers = get_manager().dispatchers
    if cmd == '/help':
        # Create a message button
        markup = InlineKeyboardMarkup()
        for ind, d in zip(range(len(dispatchers)), dispatchers):
            help_btn = _("help：{name}").format(name=d.get_name())
            markup.add(InlineKeyboardButton(help_btn, callback_data=f'help:{ind}'))
        # 帮助信息
        command_usage = [
            _("/start - Authentication"),
            _("/help - Using help"),
            _("/reload - Reload the configuration file"),
            _("/task - View, run the task"),
        ]
        help_text = \
            _("Account bill Bot\n\nAvailable instruction list：\n{command}\n\nTrade statement syntax help, select the corresponding module，Use /help [Module name] Check.").format(
                command='\n'.join(command_usage))
        bot.reply_to(message, help_text, reply_markup=markup)
    else:
        # Display detailed help
        name: str = cmd[6:]
        flag_found = False
        for d in dispatchers:
            if name.lower() == d.get_name().lower():
                show_usage_for(message, d)
                flag_found = True
        if not flag_found:
            bot.reply_to(message, _("The corresponding name of the transaction statement processor does not exist！"))


def show_usage_for(message: Message, d: Dispatcher):
    """
    Show the method of use of a specific processor
    :param message:
    :param d:
    :return:
    """
    usage = _("help：{name}\n\n{usage}").format(name=d.get_name(), usage=d.get_usage())
    bot.reply_to(message, usage)


@bot.callback_query_handler(func=lambda call: call.data[:4] == 'help')
def callback_help(call: CallbackQuery):
    """
    Help statement detailed help
    :param call:
    :return:
    """
    try:
        d_id = int(call.data[5:])
        dispatchers = get_manager().dispatchers
        show_usage_for(call.message, dispatchers[d_id])
    except Exception as e:
        logger.error(f'{call.id}：Unknown error！', e)
        logger.error(traceback.format_exc())
        bot.answer_callback_query(call.id, _("Unknown error！\n"+traceback.format_exc()))


@bot.message_handler(commands=['task'])
def task_handler(message):
    """
    Task instruction
    :param message:
    :return:
    """
    if not check_auth():
        bot.reply_to(message, _("Please conduct authentication first!"))
        return

    cmd = message.text
    tasks = get_task()
    if cmd == '/task':
        # Show all tasks
        all_tasks = ', '.join(tasks.keys())
        bot.reply_to(message,
                     _("Current registration task：{all_tasks}\n"
                       "able to pass /task [Task Name] Active trigger").format(all_tasks=all_tasks))
    else:
        # Run task
        dest = cmd[6:]
        if dest not in tasks:
            bot.reply_to(message, _("Task does not exist！"))
            return
        task = tasks[dest]
        task.trigger(bot)


#######
# trade #
#######


@bot.message_handler(func=lambda m: True)
def transaction_query_handler(message: Message):
    """
    Trading statement processing
    :param message:
    :return:
    """
    if not check_auth():
        auth_token_handler(message)
        return
    # Treated
    manager = get_manager()
    try:
        tx_uuid, tx = manager.create_from_str(message.text)
        # Create a message button
        markup = InlineKeyboardMarkup()
        markup.add(InlineKeyboardButton(_("Revoke trading"), callback_data=f'withdraw:{tx_uuid}'))
        # 回复
        bot.reply_to(message, transaction.stringfy(tx), reply_markup=markup)
    except ValueError as e:
        logger.info(f'{message.from_user.id}：Unable to add transactions', e)
        bot.reply_to(message, e.args[0])
    except Exception as e:
        logger.error(f'{message.from_user.id}：An unknown mistake!Adding a transaction failed.', e)
        bot.reply_to(message, _("An unknown mistake!Adding a transaction failed.\n"+traceback.format_exc()))


@bot.callback_query_handler(func=lambda call: call.data[:8] == 'withdraw')
def callback_withdraw(call: CallbackQuery):
    """
    Transaction withdrawal callback
    :param call:
    :return:
    """
    auth = get_session(call.from_user.id, SESS_AUTH, False)
    if not auth:
        bot.answer_callback_query(call.id, _("Please conduct authentication first！"))
        return
    tx_uuid = call.data[9:]
    manager = get_manager()
    try:
        manager.remove(tx_uuid)
        # Modify the original message reply
        message = _("Transaction has been withdrawn")
        code_format = MessageEntity('code', 0, len(message))
        bot.edit_message_text(message,
                              chat_id=call.message.chat.id,
                              message_id=call.message.message_id,
                              entities=[code_format])
    except ValueError as e:
        logger.info(f'{call.id}：Unable to create trading', e)
        bot.answer_callback_query(call.id, e.args[0])
    except Exception as e:
        logger.error(f'{call.id}：An unknown mistake!Withdrawal of the transaction failed.', e)
        bot.answer_callback_query(call.id, _("An unknown mistake!Withdrawal of the transaction failed."))


def serving():
    """
    start up Bot
    :return:
    """

    # set up Token
    token = get_config('bot.token')
    bot.token = token
    # Set a proxy
    proxy = get_config('bot.proxy')
    if proxy is not None:
        apihelper.proxy = {'https': proxy}
    # start up
    bot.infinity_polling()

[((616, 660), 'telebot.TeleBot', 'telebot.TeleBot', ([], {'token': 'None', 'parse_mode': 'None'}), '(token=None, parse_mode=None)\n', (631, 660), False, 'import telebot\n'), ((884, 921), 'beancount_bot.session.get_session_for', 'get_session_for', (['message.from_user.id'], {}), '(message.from_user.id)\n', (899, 921), False, 'from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session\n'), ((1277, 1328), 'beancount_bot.session.get_session', 'get_session', (['message.from_user.id', 'SESS_AUTH', '(False)'], {}), '(message.from_user.id, SESS_AUTH, False)\n', (1288, 1328), False, 'from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session\n'), ((1762, 1790), 'beancount_bot.config.get_config', 'get_config', (['"""bot.auth_token"""'], {}), "('bot.auth_token')\n", (1772, 1790), False, 'from beancount_bot.config import get_config, load_config\n'), ((2326, 2339), 'beancount_bot.config.load_config', 'load_config', ([], {}), '()\n', (2337, 2339), False, 'from beancount_bot.config import get_config, load_config\n'), ((2344, 2355), 'beancount_bot.task.load_task', 'load_task', ([], {}), '()\n', (2353, 2355), False, 'from beancount_bot.task import load_task, get_task\n'), ((5019, 5029), 'beancount_bot.task.get_task', 'get_task', ([], {}), '()\n', (5027, 5029), False, 'from beancount_bot.task import load_task, get_task\n'), ((5840, 5853), 'beancount_bot.transaction.get_manager', 'get_manager', ([], {}), '()\n', (5851, 5853), False, 'from beancount_bot.transaction import get_manager\n'), ((6781, 6829), 'beancount_bot.session.get_session', 'get_session', (['call.from_user.id', 'SESS_AUTH', '(False)'], {}), '(call.from_user.id, SESS_AUTH, False)\n', (6792, 6829), False, 'from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session\n'), ((6990, 7003), 'beancount_bot.transaction.get_manager', 'get_manager', ([], {}), '()\n', (7001, 7003), False, 'from beancount_bot.transaction import get_manager\n'), ((7890, 7913), 'beancount_bot.config.get_config', 'get_config', (['"""bot.token"""'], {}), "('bot.token')\n", (7900, 7913), False, 'from beancount_bot.config import get_config, load_config\n'), ((7966, 7989), 'beancount_bot.config.get_config', 'get_config', (['"""bot.proxy"""'], {}), "('bot.proxy')\n", (7976, 7989), False, 'from beancount_bot.config import get_config, load_config\n'), ((1455, 1530), 'beancount_bot.i18n._', '_', (['"""Welcome to the accounting robot!Please enter the authentication token:"""'], {}), "('Welcome to the accounting robot!Please enter the authentication token:')\n", (1456, 1530), False, 'from beancount_bot.i18n import _\n'), ((1834, 1884), 'beancount_bot.session.set_session', 'set_session', (['message.from_user.id', 'SESS_AUTH', '(True)'], {}), '(message.from_user.id, SESS_AUTH, True)\n', (1845, 1884), False, 'from beancount_bot.session import get_session, SESS_AUTH, get_session_for, set_session\n'), ((2382, 2421), 'beancount_bot.i18n._', '_', (['"""Successful overload configuration！"""'], {}), "('Successful overload configuration！')\n", (2383, 2421), False, 'from beancount_bot.i18n import _\n'), ((2603, 2616), 'beancount_bot.transaction.get_manager', 'get_manager', ([], {}), '()\n', (2614, 2616), False, 'from beancount_bot.transaction import get_manager\n'), ((2703, 2725), 'telebot.types.InlineKeyboardMarkup', 'InlineKeyboardMarkup', ([], {}), '()\n', (2723, 2725), False, 'from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, MessageEntity, Message, CallbackQuery\n'), ((5974, 5996), 'telebot.types.InlineKeyboardMarkup', 'InlineKeyboardMarkup', ([], {}), '()\n', (5994, 5996), False, 'from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, MessageEntity, Message, CallbackQuery\n'), ((7107, 7142), 'beancount_bot.i18n._', '_', (['"""Transaction has been withdrawn"""'], {}), "('Transaction has been withdrawn')\n", (7108, 7142), False, 'from beancount_bot.i18n import _\n'), ((1372, 1401), 'beancount_bot.i18n._', '_', (['"""Have been authenticated！"""'], {}), "('Have been authenticated！')\n", (1373, 1401), False, 'from beancount_bot.i18n import _\n'), ((1915, 1938), 'beancount_bot.i18n._', '_', (['"""Authentic success！"""'], {}), "('Authentic success！')\n", (1916, 1938), False, 'from beancount_bot.i18n import _\n'), ((1980, 2012), 'beancount_bot.i18n._', '_', (['"""Authentication token error！"""'], {}), "('Authentication token error！')\n", (1981, 2012), False, 'from beancount_bot.i18n import _\n'), ((2264, 2305), 'beancount_bot.i18n._', '_', (['"""Please conduct authentication first！"""'], {}), "('Please conduct authentication first！')\n", (2265, 2305), False, 'from beancount_bot.i18n import _\n'), ((2994, 3022), 'beancount_bot.i18n._', '_', (['"""/start - Authentication"""'], {}), "('/start - Authentication')\n", (2995, 3022), False, 'from beancount_bot.i18n import _\n'), ((3036, 3059), 'beancount_bot.i18n._', '_', (['"""/help - Using help"""'], {}), "('/help - Using help')\n", (3037, 3059), False, 'from beancount_bot.i18n import _\n'), ((3073, 3117), 'beancount_bot.i18n._', '_', (['"""/reload - Reload the configuration file"""'], {}), "('/reload - Reload the configuration file')\n", (3074, 3117), False, 'from beancount_bot.i18n import _\n'), ((3131, 3162), 'beancount_bot.i18n._', '_', (['"""/task - View, run the task"""'], {}), "('/task - View, run the task')\n", (3132, 3162), False, 'from beancount_bot.i18n import _\n'), ((4070, 4099), 'beancount_bot.i18n._', '_', (['"""help：{name}\n\n{usage}"""'], {}), '("""help：{name}\n\n{usage}""")\n', (4071, 4099), False, 'from beancount_bot.i18n import _\n'), ((4435, 4448), 'beancount_bot.transaction.get_manager', 'get_manager', ([], {}), '()\n', (4446, 4448), False, 'from beancount_bot.transaction import get_manager\n'), ((4552, 4596), 'beancount_bot.util.logger.error', 'logger.error', (['f"""{call.id}：Unknown error！"""', 'e'], {}), "(f'{call.id}：Unknown error！', e)\n", (4564, 4596), False, 'from beancount_bot.util import logger\n'), ((4925, 4966), 'beancount_bot.i18n._', '_', (['"""Please conduct authentication first!"""'], {}), "('Please conduct authentication first!')\n", (4926, 4966), False, 'from beancount_bot.i18n import _\n'), ((6139, 6163), 'beancount_bot.transaction.stringfy', 'transaction.stringfy', (['tx'], {}), '(tx)\n', (6159, 6163), False, 'from beancount_bot import transaction\n'), ((6222, 6290), 'beancount_bot.util.logger.info', 'logger.info', (['f"""{message.from_user.id}：Unable to add transactions"""', 'e'], {}), "(f'{message.from_user.id}：Unable to add transactions', e)\n", (6233, 6290), False, 'from beancount_bot.util import logger\n'), ((6367, 6466), 'beancount_bot.util.logger.error', 'logger.error', (['f"""{message.from_user.id}：An unknown mistake!Adding a transaction failed."""', 'e'], {}), "(\n    f'{message.from_user.id}：An unknown mistake!Adding a transaction failed.',\n    e)\n", (6379, 6466), False, 'from beancount_bot.util import logger\n'), ((6890, 6931), 'beancount_bot.i18n._', '_', (['"""Please conduct authentication first！"""'], {}), "('Please conduct authentication first！')\n", (6891, 6931), False, 'from beancount_bot.i18n import _\n'), ((7459, 7512), 'beancount_bot.util.logger.info', 'logger.info', (['f"""{call.id}：Unable to create trading"""', 'e'], {}), "(f'{call.id}：Unable to create trading', e)\n", (7470, 7512), False, 'from beancount_bot.util import logger\n'), ((7602, 7693), 'beancount_bot.util.logger.error', 'logger.error', (['f"""{call.id}：An unknown mistake!Withdrawal of the transaction failed."""', 'e'], {}), "(\n    f'{call.id}：An unknown mistake!Withdrawal of the transaction failed.', e)\n", (7614, 7693), False, 'from beancount_bot.util import logger\n'), ((2880, 2939), 'telebot.types.InlineKeyboardButton', 'InlineKeyboardButton', (['help_btn'], {'callback_data': 'f"""help:{ind}"""'}), "(help_btn, callback_data=f'help:{ind}')\n", (2900, 2939), False, 'from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, MessageEntity, Message, CallbackQuery\n'), ((3208, 3370), 'beancount_bot.i18n._', '_', (['"""Account bill Bot\n\nAvailable instruction list：\n{command}\n\nTrade statement syntax help, select the corresponding module，Use /help [Module name] Check."""'], {}), '("""Account bill Bot\n\nAvailable instruction list：\n{command}\n\nTrade statement syntax help, select the corresponding module，Use /help [Module name] Check."""\n    )\n', (3209, 3370), False, 'from beancount_bot.i18n import _\n'), ((3805, 3892), 'beancount_bot.i18n._', '_', (['"""The corresponding name of the transaction statement processor does not exist！"""'], {}), "('The corresponding name of the transaction statement processor does not exist！'\n    )\n", (3806, 3892), False, 'from beancount_bot.i18n import _\n'), ((4618, 4640), 'traceback.format_exc', 'traceback.format_exc', ([], {}), '()\n', (4638, 4640), False, 'import traceback\n'), ((5434, 5459), 'beancount_bot.i18n._', '_', (['"""Task does not exist！"""'], {}), "('Task does not exist！')\n", (5435, 5459), False, 'from beancount_bot.i18n import _\n'), ((6037, 6056), 'beancount_bot.i18n._', '_', (['"""Revoke trading"""'], {}), "('Revoke trading')\n", (6038, 6056), False, 'from beancount_bot.i18n import _\n'), ((7732, 7793), 'beancount_bot.i18n._', '_', (['"""An unknown mistake!Withdrawal of the transaction failed."""'], {}), "('An unknown mistake!Withdrawal of the transaction failed.')\n", (7733, 7793), False, 'from beancount_bot.i18n import _\n'), ((2814, 2830), 'beancount_bot.i18n._', '_', (['"""help：{name}"""'], {}), "('help：{name}')\n", (2815, 2830), False, 'from beancount_bot.i18n import _\n'), ((5173, 5270), 'beancount_bot.i18n._', '_', (['"""Current registration task：{all_tasks}\nable to pass /task [Task Name] Active trigger"""'], {}), '("""Current registration task：{all_tasks}\nable to pass /task [Task Name] Active trigger"""\n    )\n', (5174, 5270), False, 'from beancount_bot.i18n import _\n'), ((4708, 4730), 'traceback.format_exc', 'traceback.format_exc', ([], {}), '()\n', (4728, 4730), False, 'import traceback\n'), ((6542, 6564), 'traceback.format_exc', 'traceback.format_exc', ([], {}), '()\n', (6562, 6564), False, 'import traceback\n')]