from datetime import datetime, timedelta
import locale
import os

from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
    ApplicationBuilder,
    CommandHandler,
    CallbackQueryHandler,
    ContextTypes,
    MessageHandler,
    filters,
    ConversationHandler
)

from db import create_db, fetch_schedule, update_homework, insert_project, fetch_projects, fetch_project

# Инициализация бота
TOKEN = 'YOUR_TELEGRAM_BOT_TOKEN'

# Создать базу данных и таблицы
create_db()

user_state = {}

PROJECT_SUBJECT, PROJECT_NAME, PROJECT_DESCRIPTION, PROJECT_FILE = range(4)

locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8')


def format_schedule(date, schedule):
    day_name = date.strftime('%A')
    formatted = f"*Расписание на {date.strftime('%d.%m.%Y')}, {day_name}*\n\n"
    for entry in schedule:
        formatted += f"**{entry[0]}**\n" \
                     f"**{entry[1]}**\n" \
                     f"Преподаватель: {entry[2]}\n" \
                     f"Место: {entry[3]}\n" \
                     f"Домашнее задание: {entry[4]}\n\n"
    return formatted


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.effective_user.id
    today = datetime.now().date()
    user_state[user_id] = {
        'view': 'week',
        'current_date': today
    }
    await send_schedule(update, context, user_id)


async def send_schedule(update: Update, context: ContextTypes.DEFAULT_TYPE, user_id):
    state = user_state[user_id]
    view = state['view']
    current_date = state['current_date']

    if view == 'day':
        schedule = fetch_schedule(current_date.strftime('%Y-%m-%d'))
        text = format_schedule(current_date, schedule)
    elif view == 'week':
        start_of_week = current_date - timedelta(days=current_date.weekday())
        end_of_week = start_of_week + timedelta(days=6)
        text = f"*Расписание с {start_of_week.strftime('%d.%m.%Y')} по {end_of_week.strftime('%d.%m.%Y')}*\n\n"
        for single_date in (start_of_week + timedelta(n) for n in range(7)):
            schedule = fetch_schedule(single_date.strftime('%Y-%m-%d'))
            day_name = single_date.strftime('%A')  # Убедитесь, что локаль установлена на русский
            text += f"• *{single_date.strftime('%d.%m.%Y')}, {day_name}*\n"
            if schedule:
                for entry in schedule:
                    time, subject, teacher, location, homework = entry
                    homework_text = f"\nДомашнее задание: {homework}" if homework else ""
                    text += f"  - **{time}** {subject} ({teacher}){homework_text}\n"
            else:
                text += "  - Нет занятий.\n"
            text += "\n"
        text = text.strip()
    else:
        text = "Неизвестный вид отображения расписания."

    keyboard = [
        [
            InlineKeyboardButton("Неделя", callback_data='view_week'),
            InlineKeyboardButton("День", callback_data='view_day')
        ],
        [
            InlineKeyboardButton("Предыдущий", callback_data='prev'),
            InlineKeyboardButton("Следующий", callback_data='next')
        ],
        [
            InlineKeyboardButton("Добавить домашнее задание", callback_data='add_homework')
        ],
        [
            InlineKeyboardButton("Проекты", callback_data='projects_menu')
        ]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)

    if update.callback_query:
        await update.callback_query.edit_message_text(
            text=text,
            parse_mode='Markdown',
            reply_markup=reply_markup
        )
    else:
        await update.message.reply_text(
            text=text,
            parse_mode='Markdown',
            reply_markup=reply_markup
        )


async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()

    user_id = query.from_user.id
    state = user_state.get(user_id, {})

    if not state:
        state = {
            'view': 'week',
            'current_date': datetime.now().date()
        }
        user_state[user_id] = state

    if query.data == 'view_week':
        state['view'] = 'week'
        state['current_date'] = datetime.now().date()
        await send_schedule(update, context, user_id)
    elif query.data == 'view_day':
        state['view'] = 'day'
        state['current_date'] = datetime.now().date()
        await send_schedule(update, context, user_id)
    elif query.data == 'prev':
        if state['view'] == 'day':
            state['current_date'] -= timedelta(days=1)
        elif state['view'] == 'week':
            state['current_date'] -= timedelta(weeks=1)
        await send_schedule(update, context, user_id)
    elif query.data == 'next':
        if state['view'] == 'day':
            state['current_date'] += timedelta(days=1)
        elif state['view'] == 'week':
            state['current_date'] += timedelta(weeks=1)
        await send_schedule(update, context, user_id)
    elif query.data == 'add_homework':
        await send_homework_selection(update, context, user_id)
    elif query.data == 'projects_menu':
        await projects_start(update, context)
    else:
        await query.edit_message_text("Неизвестная команда.")


async def send_homework_selection(update: Update, context: ContextTypes.DEFAULT_TYPE, user_id):
    state = user_state[user_id]
    today = datetime.now().date()
    start_of_week = today - timedelta(days=today.weekday())
    dates = [(start_of_week + timedelta(days=i)) for i in range(7)]

    keyboard = []
    for date in dates:
        date_str_display = date.strftime('%d.%m.%Y')
        date_str_callback = date.strftime('%Y-%m-%d')
        keyboard.append([InlineKeyboardButton(date_str_display, callback_data=f'date_{date_str_callback}')])

    keyboard.append([InlineKeyboardButton("Отмена", callback_data='cancel')])
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.callback_query.edit_message_text(
        text="Выберите дату:",
        reply_markup=reply_markup
    )


async def select_date(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    user_id = query.from_user.id
    date_str = query.data.split('_')[1]
    selected_date = datetime.strptime(date_str, '%Y-%m-%d').date()
    user_state[user_id]['selected_date'] = selected_date

    schedule = fetch_schedule(selected_date.strftime('%Y-%m-%d'))
    if not schedule:
        await query.edit_message_text(
            text=f"На выбранную дату {selected_date.strftime('%d.%m.%Y')} нет занятий.",
            reply_markup=InlineKeyboardMarkup([[
                InlineKeyboardButton("Добавить домашнее задание", callback_data='add_homework')
            ]])
        )
        return

    homework_subjects = [{'subject': entry[1], 'time': entry[0]} for entry in schedule]
    user_state[user_id]['homework_subjects'] = homework_subjects

    keyboard = [
        [InlineKeyboardButton(f"{entry['subject']} ({entry['time']})", callback_data=f'subject_{i}')]
        for i, entry in enumerate(homework_subjects)
    ]
    keyboard.append([InlineKeyboardButton("Отмена", callback_data='cancel')])
    reply_markup = InlineKeyboardMarkup(keyboard)

    await query.edit_message_text(
        text=f"Вы выбрали дату: {selected_date.strftime('%d.%m.%Y')}. Теперь выберите предмет:",
        reply_markup=reply_markup
    )


async def select_subject(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    user_id = query.from_user.id
    data = query.data.split('_')
    if len(data) < 2:
        await query.edit_message_text(
            text="Некорректный выбор предмета.",
            reply_markup=InlineKeyboardMarkup([[
                InlineKeyboardButton("Добавить домашнее задание", callback_data='add_homework')
            ]])
        )
        return
    index = int(data[1])
    homework_subjects = user_state[user_id].get('homework_subjects', [])
    if index >= len(homework_subjects):
        await query.edit_message_text(
            text="Некорректный выбор предмета.",
            reply_markup=InlineKeyboardMarkup([[
                InlineKeyboardButton("Добавить домашнее задание", callback_data='add_homework')
            ]])
        )
        return
    selected_entry = homework_subjects[index]
    subject = selected_entry['subject']
    time_slot = selected_entry['time']
    user_state[user_id]['selected_subject'] = subject
    user_state[user_id]['selected_time'] = time_slot

    await query.edit_message_text(
        text=f"Вы выбрали предмет: {subject} ({time_slot}). Введите домашнее задание."
    )
    context.chat_data['awaiting_homework'] = True


async def handle_homework_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.effective_user.id
    if context.chat_data.get('awaiting_homework'):
        homework = update.message.text
        date = user_state[user_id].get('selected_date')
        time_slot = user_state[user_id].get('selected_time')

        if date and time_slot:
            update_homework(date.strftime('%Y-%m-%d'), time_slot, homework)

            await update.message.reply_text(f"Домашнее задание для {date.strftime('%d.%m.%Y')} {time_slot}:\n{homework}")
            await update.message.reply_text("Домашнее задание добавлено.")

            # Очистить состояние ожидания
            context.chat_data['awaiting_homework'] = False
            del user_state[user_id]['selected_date']
            del user_state[user_id]['selected_subject']
            del user_state[user_id]['selected_time']

            # После добавления домашнего задания показать расписание
            await send_schedule(update, context, user_id)
        else:
            await update.message.reply_text("Ошибка. Выберите дату и предмет.")
    else:
        await update.message.reply_text("Вы не находитесь в процессе добавления домашнего задания.")


async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    user_id = query.from_user.id
    state = user_state.get(user_id, {})

    if 'selected_date' in state or 'selected_subject' in state:
        # Удалить состояние выбора даты и предмета
        state.pop('selected_date', None)
        state.pop('selected_subject', None)
        state.pop('selected_time', None)
        await query.edit_message_text(
            text="Процесс добавления домашнего задания отменен.",
            reply_markup=InlineKeyboardMarkup([[
                InlineKeyboardButton("Добавить домашнее задание", callback_data='add_homework')
            ]])
        )
        await send_schedule(update, context, user_id)
    else:
        await query.edit_message_text(
            text="Нет активного процесса добавления домашнего задания."
        )
        await send_schedule(update, context, user_id)


async def projects_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    keyboard = [
        [InlineKeyboardButton("Просмотреть проекты", callback_data='projects_view')],
        [InlineKeyboardButton("Добавить проект", callback_data='projects_add')],
        [InlineKeyboardButton("Назад", callback_data='back_to_schedule')]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await query.edit_message_text(
        text="Меню проектов:",
        reply_markup=reply_markup
    )


async def projects_button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    data = query.data

    if data == 'projects_view':
        await show_projects(update, context)
    elif data == 'projects_add':
        await add_project_start(update, context)
        return PROJECT_SUBJECT  # Начало разговора по добавлению проекта
    elif data == 'back_to_schedule':
        user_id = query.from_user.id
        await send_schedule(update, context, user_id)
    elif data.startswith('project_view_'):
        project_id = int(data.split('_')[-1])
        await view_project(update, context, project_id)
    elif data.startswith('project_download_'):
        project_id = int(data.split('_')[-1])
        await download_project_file(update, context, project_id)
    else:
        await query.edit_message_text("Неизвестная команда.")


async def show_projects(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    projects = fetch_projects()
    if not projects:
        await query.edit_message_text("Проекты не найдены.", reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("Назад", callback_data='projects_menu')]
        ]))
    else:
        text = "Список проектов:\n"
        keyboard = []
        for project in projects:
            project_id, subject, project_name, description, file_path = project
            text += f"{project_id}. {project_name} ({subject})\n"
            keyboard.append([InlineKeyboardButton(f"{project_name}", callback_data=f'project_view_{project_id}')])
        keyboard.append([InlineKeyboardButton("Назад", callback_data='projects_menu')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(text=text, reply_markup=reply_markup)


async def view_project(update: Update, context: ContextTypes.DEFAULT_TYPE, project_id):
    query = update.callback_query
    project = fetch_project(project_id)
    if not project:
        await query.edit_message_text("Проект не найден.", reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("Назад", callback_data='projects_view')]
        ]))
    else:
        project_id, subject, project_name, description, file_path = project
        text = f"Проект: {project_name}\nПредмет: {subject}\nОписание: {description}"

        keyboard = []
        if file_path:
            keyboard.append([InlineKeyboardButton("Скачать файл", callback_data=f'project_download_{project_id}')])
        keyboard.append([InlineKeyboardButton("Назад", callback_data='projects_view')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        await query.edit_message_text(text=text, reply_markup=reply_markup)


async def download_project_file(update: Update, context: ContextTypes.DEFAULT_TYPE, project_id):
    query = update.callback_query
    project = fetch_project(project_id)
    if not project:
        await query.edit_message_text("Проект не найден.", reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("Назад", callback_data='projects_view')]
        ]))
    else:
        file_path = project[4]
        if not file_path or not os.path.exists(file_path):
            await query.edit_message_text("Файл для этого проекта не найден.", reply_markup=InlineKeyboardMarkup([
                [InlineKeyboardButton("Назад", callback_data='projects_view')]
            ]))
        else:
            await context.bot.send_document(chat_id=query.message.chat_id, document=open(file_path, 'rb'))
            await query.edit_message_text("Файл отправлен.", reply_markup=InlineKeyboardMarkup([
                [InlineKeyboardButton("Назад", callback_data='projects_view')]
            ]))


async def add_project_start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.callback_query.edit_message_text("Введите название предмета для проекта:")
    return PROJECT_SUBJECT


async def add_project_subject(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data['project_subject'] = update.message.text
    await update.message.reply_text("Введите название проекта:")
    return PROJECT_NAME


async def add_project_name(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data['project_name'] = update.message.text
    await update.message.reply_text("Введите описание проекта:")
    return PROJECT_DESCRIPTION


async def add_project_description(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data['project_description'] = update.message.text
    keyboard = [[InlineKeyboardButton("Пропустить", callback_data='skip_file')]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text("Загрузите файл проекта или нажмите 'Пропустить'.", reply_markup=reply_markup)
    return PROJECT_FILE


async def add_project_file(update: Update, context: ContextTypes.DEFAULT_TYPE):
    document = update.message.document
    file = await context.bot.get_file(document.file_id)
    # Создаем директорию files, если ее нет
    if not os.path.exists('files'):
        os.makedirs('files')
    file_path = f"files/{document.file_name}"
    await file.download_to_drive(file_path)
    context.user_data['project_file_path'] = file_path
    # Сохраняем проект
    insert_project(
        context.user_data['project_subject'],
        context.user_data['project_name'],
        context.user_data['project_description'],
        context.user_data.get('project_file_path')
    )
    await update.message.reply_text("Проект успешно добавлен.")
    return ConversationHandler.END


async def skip_project_file(update: Update, context: ContextTypes.DEFAULT_TYPE):
    # Сохраняем проект без файла
    insert_project(
        context.user_data['project_subject'],
        context.user_data['project_name'],
        context.user_data['project_description'],
        None
    )
    await update.callback_query.edit_message_text("Проект успешно добавлен без файла.")
    return ConversationHandler.END


async def cancel_project_addition(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("Добавление проекта отменено.")
    return ConversationHandler.END


def main():
    application = ApplicationBuilder().token(TOKEN).build()

    application.add_handler(CommandHandler("start", start))

    # Обработчики домашнего задания
    application.add_handler(CallbackQueryHandler(select_date, pattern='^date_'))
    application.add_handler(CallbackQueryHandler(select_subject, pattern='^subject_'))
    application.add_handler(CallbackQueryHandler(cancel, pattern='^cancel$'))

    # Обработчик проекта
    project_conv_handler = ConversationHandler(
        entry_points=[CallbackQueryHandler(add_project_start, pattern='^projects_add$')],
        states={
            PROJECT_SUBJECT: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_project_subject)],
            PROJECT_NAME: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_project_name)],
            PROJECT_DESCRIPTION: [MessageHandler(filters.TEXT & ~filters.COMMAND, add_project_description)],
            PROJECT_FILE: [
                MessageHandler(filters.Document.ALL, add_project_file),
                CallbackQueryHandler(skip_project_file, pattern='^skip_file$')
            ],
        },
        fallbacks=[CommandHandler('cancel', cancel_project_addition)]
    )
    application.add_handler(project_conv_handler)

    # Общий обработчик кнопок
    application.add_handler(CallbackQueryHandler(projects_button_handler, pattern='^projects_'))
    application.add_handler(CallbackQueryHandler(button))

    # Обработчик текстовых сообщений
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_homework_text))

    application.run_polling()


if __name__ == '__main__':
    main()






PTBUserWarning: If 'per_message=False', 'CallbackQueryHandler' will not be tracked for every message. Read this FAQ entry to learn more about the per_* settings: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Frequently-Asked-Questions#what-do-the-per_-settings-in-conversationhandler-do.
  project_conv_handler = ConversationHandler(