import asyncio
import datetime
import logging
import re
import sqlite3
import aiogram
import requests
from aiogram import Bot, Dispatcher
from aiogram import types
from aiogram.contrib.fsm_storage.memory import MemoryStorage
from aiogram.dispatcher import FSMContext
from aiogram.dispatcher.filters.state import State, StatesGroup
from aiogram.utils import exceptions
from aiogram.utils import executor
from aiogram.utils.markdown import text
from apscheduler.schedulers.asyncio import AsyncIOScheduler
import json
import config
import keyboards

helps = text(f"🧑‍💻<b>Техническая помощь, возражения и предложения:</b>",
             f"\n📞<b>Связь:</b> @vo7_admin",
             f"\n🏠<b>Домой:</b> /start")

db = sqlite3.connect('users.sqlite')

connect = db.cursor()
connect.execute('''CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id TEXT,
  chat_id TEXT,
  bot_message_language TEXT,
  language_code TEXT,
  message_id TEXT,

  is_bot TEXT,
  first_name TEXT,
  username TEXT,
  role TEXT,

  text TEXT,
  type TEXT,
  balance FLOAT,
  reg_date TEXT,
  orders INTEGER,
  UNIQUE (chat_id)
)''')

connect.execute('''CREATE TABLE IF NOT EXISTS posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id TEXT,
  message_id TEXT,
  text TEXT,
  
  model TEXT,
  username TEXT,
  city TEXT,
  description TEXT,
  trade_date TEXT,
  whats_trade INTEGER,
  UNIQUE (message_id)
)''')

# Configure logging
logging.basicConfig(level=logging.INFO)

# logging.basicConfig(level=logging.INFO)
log = logging.getLogger('broadcast')

# For example use simple MemoryStorage for Dispatcher.
storage = MemoryStorage()

# Initialize bot and dispatcher
bot = Bot(token=config.API_TOKEN)
dp = Dispatcher(bot, storage=storage)


# States
class Form(StatesGroup):
    model = State()
    city = State()
    description = State()
    trade_date = State()
    whats_trade = State()


def data():
    parse_date = str(datetime.datetime.now().date()).replace("datetime.date", "").replace("(", "").replace(")", "")
    parse_time = str(datetime.datetime.now().time()).split(".")
    parse_time = parse_time[0]
    return [parse_time, parse_date]


def get_users():
    """
    Return users list

    In this example returns some random ID's
    """
    yield from (451658425, 608551286, 78378343, 98765431, 12345678)


async def send_messages(user_id: int, text: str, disable_notification: bool = False) -> bool:
    """
    Safe messages sender

    :param user_id:
    :param text:
    :param disable_notification:
    :return:
    """
    try:
        await bot.send_message(user_id, text, disable_notification=disable_notification)
    except exceptions.BotBlocked:
        log.error(f"Target [ID:{user_id}]: blocked by user")
    except exceptions.ChatNotFound:
        log.error(f"Target [ID:{user_id}]: invalid user ID")
    except exceptions.RetryAfter as e:
        log.error(f"Target [ID:{user_id}]: Flood limit is exceeded. Sleep {e.timeout} seconds.")
        await asyncio.sleep(e.timeout)
        return await send_messages(user_id, text)  # Recursive call
    except exceptions.UserDeactivated:
        log.error(f"Target [ID:{user_id}]: user is deactivated")
    except exceptions.TelegramAPIError:
        log.exception(f"Target [ID:{user_id}]: failed")
    else:
        log.info(f"Target [ID:{user_id}]: success")
        return True
    return False


async def broadcaster() -> int:
    """
    Simple broadcaster

    :return: Count of messages
    """
    count = 0
    try:
        for user_id in get_users():
            if await send_messages(user_id, '<b>Hello!</b>'):
                count += 1
            await asyncio.sleep(.05)  # 20 messages per second (Limit: 30 messages per second)
    finally:
        log.info(f"{count} messages successful sent.")

    return count


# ----------- Commands ----------- #
# отвечаем на нажатие старт
@dp.message_handler(commands=['start'], chat_type='private')
async def start_cmd_message(message: types.Message):
    me = (await bot.get_me()).username  # username бота
    # получаем дату регестрации
    reg_date = data()
    reg_date = reg_date[0] + " " + reg_date[1]
    # код для поиска сообщения по ид
    with open("message_urls") as openfile:
        for line in openfile:
            if f'{message.from_user.id}' in line:
                txt_message_id = int(line.replace('\n', '').replace(" ", "").split(",")[1])
                try:
                    await bot.delete_message(chat_id=message.from_user.id, message_id=txt_message_id)
                except aiogram.utils.exceptions.MessageToDeleteNotFound:
                    pass
                except aiogram.utils.exceptions.MessageCantBeDeleted:
                    pass
    openfile.close()

    # код для удаления из текстовика лишней инфы
    with open('message_urls') as f:
        lines = f.readlines()

    pattern = re.compile(re.escape(str(message.from_user.id)))
    with open('message_urls', 'w') as f:
        for line in lines:
            result = pattern.search(line)
            if result is None:
                f.write(line)

    if message.sender_chat is not None:  # проверяем канал ли это.
        # если не канал, добавляем как юзера или группу, супергруппу
        # Для записи в бдшку
        message_id = message.message_id
        username = message.sender_chat.username
        is_bot = 0
        language_code = 0
        chat_id = message.sender_chat.id
        user_id = message.from_user.id
        first_name = message.from_user.first_name
        texts = message.text
        type1 = message.sender_chat.type
        bot_message_language = 0
        balance = 0
        orders = 0
        role = None

        # добавляем данные о юзере в базу данных
        day_limit = 50

        inf_list = (user_id, chat_id, bot_message_language, language_code, message_id, is_bot,
                    first_name, username, role, texts, type1, balance, reg_date, orders)
        try:
            connect.execute('''INSERT INTO users(user_id, chat_id, bot_message_language, language_code, message_id,
             is_bot, first_name, username, role, text, type, balance, reg_date, orders)
               VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', inf_list)
            db.commit()
        except sqlite3.IntegrityError:
            pass  # если пользователь уже в базе пропускаем

    else:
        # если не канал, добавляем как юзера или группу, супергруппу
        # Для записи в бдшку
        message_id = message.message_id
        username = message.chat.username
        is_bot = message.from_user.is_bot
        language_code = message.from_user.language_code
        chat_id = message.chat.id
        user_id = message.from_user.id
        first_name = message.from_user.first_name
        texts = message.text
        type1 = message.chat.type
        bot_message_language = 0
        balance = 0
        orders = 0

        role = None

        # добавляем данные о юзере в базу данных

        inf_list = (user_id, chat_id, bot_message_language, language_code, message_id, is_bot,
                    first_name, username, role, texts, type1, balance, reg_date, orders)
        try:
            connect.execute('''INSERT INTO users(user_id, chat_id, bot_message_language, language_code, message_id,
             is_bot, first_name, username, role, text, type, balance, reg_date, orders)
               VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', inf_list)
            db.commit()
        except sqlite3.IntegrityError:
            pass  # если пользователь уже в базе пропускаем

        txt_export = open("message_urls", 'a+')

        # Export users в txt файл and send message to admin
        user_id = message.chat.id

        # записываем сообщение с коммандой старт
        txt_export.write(
            f"{str(message.chat.id)}, {str(message.message_id)}\n")  # записываем id в текстовый файл, all users

        # записываем сообщение с кнопкой подписки
        button_mess_ids = int(message.message_id)
        button_mess_ids += 1
        txt_export.write(
            f"{str(message.chat.id)}, {str(button_mess_ids)}\n")  # записываем id в текстовый файл, all users

        txt_export.close()

    start_message = f"✌🏻<b>Привет, {message.chat.first_name}! Я бот-барахолки!</b>" \
                    f"\n‼️<b>Я могу осуществлять авто-публикацию.</b>"
    await bot.send_message(chat_id, text=start_message, parse_mode='HTML', reply_markup=keyboards.main_menu())


# отвечаем на нажатие help
@dp.message_handler(commands=['help'], chat_type='private')
async def start_cmd_message(message: types.Message):
    await send_messages(message.chat.id, helps, parse_mode='HTML')


@dp.message_handler(text=['🔰 Помощь'], chat_type='private')
async def start_cmd_message(message: types.Message):
    chat_id = message.chat.id
    username = message.from_user.first_name
    await bot.send_message(chat_id, text=helps, reply_markup=keyboards.main_menu(), parse_mode='HTML')


@dp.message_handler(text=['💲 Донат'], chat_type='private')
async def start_cmd_message(message: types.Message):
    chat_id = message.chat.id
    username = message.from_user.first_name
    await bot.send_message(chat_id, text=f"🙏🏻<b>Кошельки для доната:</b>"
                                         f"\n💰<b>BTC -</b> "
                                         f"\n💰<b>USDT (TRC-20) TRON</b> - "
                                         f"", reply_markup=keyboards.main_menu(), parse_mode='HTML')


@dp.message_handler(text=['👁‍🗨 VO7.BY - БАРАХОЛКА APPLE'], chat_type='private')
async def start_cmd_message(message: types.Message):
    chat_id = message.chat.id
    await bot.send_message(chat_id, text=text(f"https://t.me/APPLE_BELARUS"
                                              f"\n😎<b>Администрация:</b> @vo7_admin"
                                              f"\n<b>Это сообщество для:</b>"
                                              f"\n<b>- продажи запчастей</b>"
                                              f"\n<b>- продажи техники apple</b>"
                                              f"\n<b>- консультации</b>"
                                              f"\n\n🌐<b>Сайт:</b>"
                                              f"\nwww.vo7.by"
                                              f"\n🏪<b>Вконтакте:</b>"
                                              f"\nhttps://vk.com/vo7by"
                                              f"\n📷<b>Интаграм:</b>"
                                              f"\nInstagram.com/apple_belarus_minsk"),
                           reply_markup=keyboards.main_menu(), parse_mode='HTML')


@dp.message_handler(text=['🔝 Добавить Объявление'], chat_type='private')
async def drop_video_id(call: types.CallbackQuery, state: FSMContext):
    chat_id = call.from_user.id
    first_name = call.from_user.first_name
    await bot.delete_message(chat_id=chat_id, message_id=call.message_id)

    mes_1 = await bot.send_message(chat_id, text=f"📝️Пожалуйста, заполните 5ть пунктов: "
                                                 f"\n“"
                                                 f"\n<b>- Модель:</b>"
                                                 f"\n<b>- Город:</b>"
                                                 f"\n<b>- Описание:</b>"
                                                 f"\n<b>- Когда желаете обменяться (05.10.2022):</b>"
                                                 f"\n<b>- На что:</b>"
                                                 f"\n“"
                                                 f"\n\nВаше предложение будет опубликовано в группе: "
                                                 f"@apple_belarus"
                                                 f"\n\nСпасибо за понимание!",
                                   reply_markup=keyboards.order_cancel(), parse_mode='HTML')
    mes_2 = await bot.send_message(chat_id, text=f"1.📌<b>Введите модель устройства, которое желаете предложить</b> "
                                                 f"<i>(пример: Apple iPhone 11 64gb Black):</i> ", parse_mode='HTML')
    await state.update_data(user_info=[chat_id, first_name, call.from_user.username],
                            posts_id_1=[mes_1.message_id, mes_2.message_id])
    await Form.model.set()


# если текст
@dp.message_handler(state=Form.model, content_types=['text'])
async def process_name_1(message: types.Message, state: FSMContext):
    current_state = await state.get_data()
    chat_id = message.chat.id
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    if "❌Отмена" in message.text or '❌Cancel' in message.text:  # при нажатие на кнопку отмена возвращаемся в меню
        await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                               parse_mode='HTML')
        await state.finish()  # останавливаем state в любом случае в конце подпрограммы
    else:
        if len(message.text) <= 2:  # если текст слишком мал требуем повторного ввода
            await bot.send_message(chat_id, text="<i>Слишком короткий текст.Попробуйте еще раз:</i>", parse_mode='HTML')
        else:
            posts_id_0 = int(current_state['posts_id_1'][0])
            posts_id_1 = int(current_state['posts_id_1'][1])

            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_0)
            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_1)

            mes_1 = await bot.send_message(chat_id,
                                           text=f"2.📌<b>Введите город в котором желаете обменять Ваше предложение</b> "
                                                f"<i>(например: Минск):</i>", parse_mode='HTML')
            await state.update_data(model=[message.text], posts_id_2=[int(mes_1.message_id)])
            await Form.city.set()


@dp.message_handler(state=Form.city, content_types=['text'])
async def process_name_2(message: types.Message, state: FSMContext):
    current_state = await state.get_data()
    chat_id = message.chat.id
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    if "❌Отмена" in message.text or '❌Cancel' in message.text:  # при нажатие на кнопку отмена возвращаемся в меню
        await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                               parse_mode='HTML')
        await state.finish()  # останавливаем state в любом случае в конце подпрограммы
    else:
        if len(message.text) <= 2:  # если текст слишком мал требуем повторного ввода
            await bot.send_message(chat_id, text="<i>Слишком короткий текст.Попробуйте еще раз:</i>", parse_mode='HTML')
        else:
            posts_id_0 = int(current_state['posts_id_2'][0])

            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_0)

            mes_1 = await bot.send_message(chat_id, text=f"3.📌<b>Опишите предложение, сильные и слабые стороны</b> "
                                                         f"<i>(например: разбит экран, есть потертости по корпусу, из"
                                                         f" комплекта осталась только коробка):</i>", parse_mode='HTML')
            await state.update_data(sity=[message.text], posts_id=[mes_1.message_id])
            await Form.description.set()


@dp.message_handler(state=Form.description, content_types=['text'])
async def process_name_3(message: types.Message, state: FSMContext):
    current_state = await state.get_data()
    chat_id = message.chat.id
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    if "❌Отмена" in message.text or '❌Cancel' in message.text:  # при нажатие на кнопку отмена возвращаемся в меню
        await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                               parse_mode='HTML')
        await state.finish()  # останавливаем state в любом случае в конце подпрограммы
    else:
        if len(message.text) <= 2:  # если текст слишком мал требуем повторного ввода
            await bot.send_message(chat_id, text="<i>Слишком короткий текст.Попробуйте еще раз:</i>", parse_mode='HTML')
        else:
            posts_id_0 = int(current_state['posts_id_2'][0])

            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_0)

            mes_1 = await bot.send_message(chat_id, text=f"4.📌<b>Введите дату, когда желаете обменять/продать Ваше"
                                                         f" предложение</b> <i>(например: через три недели/завтра):</i>",
                                           parse_mode='HTML')
            await state.update_data(description=[message.text], posts_id=[mes_1.message_id])
            await Form.trade_date.set()


@dp.message_handler(state=Form.trade_date, content_types=['text'])
async def process_name_4(message: types.Message, state: FSMContext):
    current_state = await state.get_data()
    chat_id = message.chat.id
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    if "❌Отмена" in message.text or '❌Cancel' in message.text:  # при нажатие на кнопку отмена возвращаемся в меню
        await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                               parse_mode='HTML')
        await state.finish()  # останавливаем state в любом случае в конце подпрограммы
    else:
        if len(message.text) <= 2:  # если текст слишком мал требуем повторного ввода
            await bot.send_message(chat_id, text="<i>Слишком короткий текст.Попробуйте еще раз:</i>", parse_mode='HTML')
        else:
            posts_id_0 = int(current_state['posts_id_2'][0])

            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_0)

            mes_1 = await bot.send_message(chat_id, text=f"5.📌 <b>На что желаете обменять свое предложение</b> "
                                                         f"<i>(например: 1000 BYN / 14 Pro Max 256gb):</i>",
                                           parse_mode='HTML')
            await state.update_data(trade_date=[message.text], posts_id=[mes_1.message_id])
            await Form.whats_trade.set()


@dp.message_handler(state=Form.whats_trade, content_types=['text'])
async def process_name_5(message: types.Message, state: FSMContext):
    current_state = await state.get_data()
    chat_id = message.chat.id
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    if "❌Отмена" in message.text or '❌Cancel' in message.text:  # при нажатие на кнопку отмена возвращаемся в меню
        await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                               parse_mode='HTML')
        await state.finish()  # останавливаем state в любом случае в конце подпрограммы
    else:
        if len(message.text) <= 2:  # если текст слишком мал требуем повторного ввода
            await bot.send_message(chat_id, text="<i>Слишком короткий текст.Попробуйте еще раз:</i>", parse_mode='HTML')
        else:
            user_id = str(current_state['user_info'][0])
            first_name = str(current_state['user_info'][1])
            username = str(current_state['user_info'][2])
            model = str(current_state['model'][0])
            city = str(current_state['sity'][0])
            description = str(current_state['description'][0])
            trade_date = str(current_state['trade_date'][0])

            # await bot.delete_message(chat_id=message.chat.id, message_id=posts_id_0)
            whats_trade = message.text
            #  f"\n🪪ID: <code>{user_id}</code>"
            mes_1 = await bot.send_message(config.drop_chat_id, text=f"📱<b>Модель:</b> <code>{model}</code>"
                                                                     f"\n🪪<b>Имя аккаунта:</b> {first_name} @{username}"
                                                                     f"\n🏢<b>Город:</b> <code>{city}</code>"
                                                                     f"\n📃<b>Описание:</b> <code>{description}</code>"
                                                                     f"\n🕑<b>Когда желаете обменяться:</b> <code>{trade_date}</code>"
                                                                     f"\n🤝<b>Хочу обменять на:</b> <code>{whats_trade}</code>"
                                                                     f"\n\n🤖<b>Опубликовать объявление:</b> @vo7_bot",
                                           parse_mode='HTML')
            mes_2 = await bot.send_message(chat_id, text=f"🎉 Ура! Ваше предложение уже опубликовано в группе "
                                                         f"@apple_belarus ", parse_mode='HTML',
                                           reply_markup=keyboards.main_menu())

            inf_list = (user_id, mes_2.message_id, mes_1.text, model, username, city, description, trade_date,
                        whats_trade)
            db.execute("INSERT INTO posts (user_id, message_id, text, model, username, city, description, trade_date,"
                       " whats_trade) VALUES(?,?,?,?,?,?,?,?,?)", inf_list)
            db.commit()
            await state.finish()


@dp.message_handler(text=['❌Отмена', '❌Cancel'], chat_type='private')
async def start_cmd_message(message: types.Message):
    # await message.delete()
    chat_id = message.chat.id
    username = message.from_user.first_name
    start_message = f"⏹Вы вернулись в главное меню. Можете продолжить пользование ботом."
    await bot.send_message(chat_id, text=start_message, reply_markup=keyboards.main_menu(),
                           parse_mode='HTML')


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    executor.start_polling(dp, skip_updates=False)

# See PyCharm help at https://www.jetbrains.com/help/pycharm/
