from telebot import types
from telebot.async_telebot import Handler, AsyncTeleBot
from telebot.asyncio_handler_backends import StatesGroup, State, ContinueHandling
from telebot.types import ReplyKeyboardRemove

from account.models import User
from tg_bot.messages import TGCoreMessage
from tg_bot.keyboards import TGKeyboards
from tg_bot.tasks import send_tg_message
from tg_bot.utils import extract_deep_link


# TODO: step handlers (https://github.com/eternnoir/pyTelegramBotAPI/blob/master/examples/step_example.py)
# TODO: state filters
class BotStates(StatesGroup):
    idle = State()
    send_phone = State()


# Default message handler
async def default_handler(message: types.Message, bot: AsyncTeleBot):
    cid = message.from_user.id

    # Set default state for sender
    if await bot.get_state(cid) is None:
        await bot.set_state(cid, BotStates.idle)

    return ContinueHandling()


# === Command /start ===
async def start_handler(message: types.Message, bot: AsyncTeleBot):
    cid = message.from_user.id

    await bot.set_state(cid, BotStates.idle)

    if not await is_tg_bound_to_user(cid):
        # Send welcome message
        await bot.send_message(cid, TGCoreMessage.START)
        referral_code = extract_deep_link(message.text)

        # Save referral code in chat context for later usage
        # TODO: use base64-encoded hash here for additional data maybe?
        async with bot.retrieve_data(cid) as data:
            data['referral_code'] = referral_code

        # Ask for phone number to bind to User
        await request_phone(cid, TGCoreMessage.SHARE_PHONE, bot)

    else:
        # TODO: if user is signed in, send help text or account info instead
        await bot.send_message(cid, TGCoreMessage.ALREADY_AUTH)


# TODO: implement sometime in the future
# === Command /add_phone ===
# async def add_phone_handler(message: types.Message, bot: AsyncTeleBot):
#     cid = message.from_user.id
#     await request_phone(cid, bot, TGCoreMessage.SHARE_PHONE)


async def is_tg_bound_to_user(tg_user_id):
    return await User.objects.filter(tg_user_id=tg_user_id, phone__isnull=False).aexists()


async def request_phone(user_id, text, bot: AsyncTeleBot):
    """ Ask for phone number to bind to User """
    reply_markup = TGKeyboards.SHARE_PHONE

    await bot.set_state(user_id, BotStates.send_phone)
    await bot.send_message(user_id, text, reply_markup=reply_markup)


def request_phone_sync(user_id, text):
    state = BotStates.send_phone
    reply_markup = TGKeyboards.SHARE_PHONE

    send_tg_message(user_id, text, state=state.name, reply_markup=reply_markup)


async def contact_handler(message: types.Message, bot: AsyncTeleBot):
    """ User has sent his phone number to authenticate in bot """

    cid = message.from_user.id

    async with bot.retrieve_data(cid) as data:
        await User.objects.bind_tg_user(
            tg_user_id=cid,
            phone=message.contact.phone_number,
            referral_code=data.pop('referral_code', None)
        )
        await bot.send_message(cid, TGCoreMessage.AUTH_SUCCESS, reply_markup=ReplyKeyboardRemove())

    await bot.set_state(cid, BotStates.idle)


def get_handlers():
    return [
        Handler(callback=default_handler),
        Handler(callback=start_handler, commands=['start'], state=BotStates.idle),
        # Handler(callback=add_phone_handler, commands=['add_phone']),
        Handler(callback=contact_handler,
                content_types=['contact'],
                state=BotStates.send_phone,
                func=lambda m: m.from_user.id == m.contact.user_id  # Disallow contact forwarding
                ),
    ]


"""
- backend: try to find User with tg_user_id of Message
- no User found -> ask for phone

Possible situations:
1) No user in db: 
- tg: ask for phone
- backend: create draft user
- web: login via tg -> 

2) User exists in DB, but has no phone (phone deleted in web)
- tg: ask for phone
- find User with tg_user_id & add phone

3) User exists in DB, but no tg_user_id or phone  ???

"""

"""
Options:
- login via telegram -> find User with tg_user_id -> if found, login
- send contact from bot -> find user with phone -> add tg_user_id to User
- 
"""
