import json
import logging
from datetime import datetime
from random import shuffle
import os
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (
    ApplicationBuilder,
    CommandHandler,
    ContextTypes,
    CallbackQueryHandler,
    MessageHandler,
    filters,
)
from dotenv import load_dotenv

# Загрузка переменных окружения из .env файла
load_dotenv()
BOT_TOKEN = os.getenv('BOT_TOKEN')

# Настройка логирования
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    level=logging.INFO
)
logger = logging.getLogger(__name__)

# Список участников группы
GROUP_MEMBERS = [
    "Ватутин Михаил Иванович",
    "Дружинин Вячеслав Владимирович",
    "Дьяков Кирилл Андреевич",
    "Евдокимов Сергей Иванович",
    "Заикин Игнат Олегович",
    "Касаткин Евгений Дмитриевич",
    "Леохин Юрий Иванович",
    "Морозов Ярослав Вадимович",
    "Новиков Артём Русланович",
    "Овчинникова Дарья Витальевна",
    "Пинаев Кирилл Сергеевич",
    "Плотников Евгений Александрович",
    "Рогачева Ирина Максимовна",
    "Рудников Никита Алексеевич",
    "Сидельников Владислав Сергеевич",
    "Солодков Константин Вячеславович",
    "Туровский Даниил Антонович",
    "Уколов Михаил Александрович",
    "Усольцев Юрий Алексеевич"
]

DATA_FILE = 'data.json'

# Функция загрузки данных с автоматической генерацией файла при отсутствии
def load_data():
    if not os.path.exists(DATA_FILE):
        # Инициализация структуры данных
        data = {
            "attendance": {},
            "duties": {}
        }
        save_data(data)
        logger.info(f"{DATA_FILE} был создан автоматически.")
        return data
    try:
        with open(DATA_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    except json.JSONDecodeError:
        # Если файл повреждён или пуст, переинициализируем его
        data = {
            "attendance": {},
            "duties": {}
        }
        save_data(data)
        logger.warning(f"{DATA_FILE} был повреждён и переинициализирован.")
        return data

# Функция сохранения данных
def save_data(data):
    with open(DATA_FILE, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
    logger.info(f"Данные сохранены в {DATA_FILE}.")

# Команда /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(
        "Привет! Я помогу тебе управлять посещаемостью и дежурствами.\n\n"
        "Доступные команды:\n"
        "/mark — отметить посещаемость сегодня\n"
        "/stats — показать статистику посещаемости\n"
        "/duty — выбрать дежурных"
    )

# Команда /mark
async def mark_attendance(update: Update, context: ContextTypes.DEFAULT_TYPE):
    data = load_data()
    user = update.message.from_user
    chat_id = update.message.chat_id

    # Проверим, что пользователь в группе
    if user.full_name not in GROUP_MEMBERS:
        await update.message.reply_text("Вы не в списке участников группы.")
        return

    today = datetime.now().strftime("%Y-%m-%d")
    if today not in data["attendance"]:
        # Инициализируем список для сегодняшнего дня
        data["attendance"][today] = {member: "Присутствовал" for member in GROUP_MEMBERS}

    await update.message.reply_text(
        "Отметьте посещаемость:\n"
        "Отправьте название по фамилии и выберите статус:\n"
        "N — отсутствовал\n"
        "O — опоздал\n"
        "Например: Ватутин N"
    )

    save_data(data)

# Обработка сообщений для отметки посещаемости
async def handle_attendance(update: Update, context: ContextTypes.DEFAULT_TYPE):
    data = load_data()
    user = update.message.from_user
    message = update.message.text.strip()

    today = datetime.now().strftime("%Y-%m-%d")
    if today not in data["attendance"]:
        await update.message.reply_text("Сначала используйте команду /mark для отметки посещаемости.")
        return

    try:
        name, status = message.rsplit(' ', 1)
        name = name.strip()
        status = status.upper()

        if name not in GROUP_MEMBERS:
            await update.message.reply_text("Участника с таким именем нет в группе.")
            return

        if status not in ['N', 'O']:
            await update.message.reply_text("Неверный статус. Используйте N или O.")
            return

        if status == 'N':
            data["attendance"][today][name] = "Не присутствовал"
        elif status == 'O':
            data["attendance"][today][name] = "Опоздал"

        save_data(data)
        await update.message.reply_text(f"Отметка для {name} обновлена до '{data['attendance'][today][name]}'.")
    except ValueError:
        await update.message.reply_text("Неверный формат. Пример: Ватутин N")

# Команда /stats
async def show_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):
    data = load_data()
    if not data["attendance"]:
        await update.message.reply_text("Нет данных о посещаемости.")
        return

    stats = {}
    for member in GROUP_MEMBERS:
        stats[member] = {"Присутствовал": 0, "Опоздал": 0, "Не присутствовал": 0}

    for day, records in data["attendance"].items():
        for member, status in records.items():
            if status in stats[member]:
                stats[member][status] += 1

    message = "Статистика посещаемости:\n\n"
    for member, counts in stats.items():
        message += (
            f"{member}:\n"
            f"  Присутствовал: {counts['Присутствовал']}\n"
            f"  Опоздал: {counts['Опоздал']}\n"
            f"  Не присутствовал: {counts['Не присутствовал']}\n\n"
        )

    await update.message.reply_text(message)

# Команда /duty
async def duty_selection(update: Update, context: ContextTypes.DEFAULT_TYPE):
    keyboard = [
        [InlineKeyboardButton("Выбрать дежурных", callback_data='select_duty')]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text('Нажмите кнопку ниже для выбора дежурных:', reply_markup=reply_markup)

# Обработка нажатий на кнопки
async def button(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()

    if query.data == 'select_duty':
        duty_persons = select_duty_persons()
        message = "Дежурные на сегодня:\n" + "\n".join(duty_persons)
        await query.edit_message_text(text=message)

# Функция выбора дежурных с равномерным распределением
def select_duty_persons():
    data = load_data()
    today = datetime.now().strftime("%Y-%m-%d")

    if today in data["duties"]:
        # Если дежурные уже выбраны на сегодня, вернуть их
        return data["duties"][today]

    # Подсчет количества дежурств для каждого участника
    duty_counts = {member: 0 for member in GROUP_MEMBERS}
    for duties_day, duties in data["duties"].items():
        for member in duties:
            if member in duty_counts:
                duty_counts[member] += 1

    # Сортировка участников по количеству дежурств
    sorted_members = sorted(GROUP_MEMBERS, key=lambda x: duty_counts[x])

    # Выбор двух участников с наименьшим количеством дежурств
    selected = sorted_members[:2]

    # В случайном порядке для большей справедливости
    shuffle(selected)

    # Обновление данных
    data["duties"][today] = selected
    save_data(data)

    return selected

# Главная функция
def main():
    # Убедитесь, что BOT_TOKEN загружен из переменных окружения
    if not BOT_TOKEN:
        logger.error("Токен бота не найден! Убедитесь, что он установлен в переменных окружения.")
        return

    application = ApplicationBuilder().token(BOT_TOKEN).build()

    # Регистрируем обработчики команд
    application.add_handler(CommandHandler('start', start))
    application.add_handler(CommandHandler('mark', mark_attendance))
    application.add_handler(CommandHandler('stats', show_stats))
    application.add_handler(CommandHandler('duty', duty_selection))

    # Обработчик нажатий на кнопки
    application.add_handler(CallbackQueryHandler(button))

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

    # Запуск бота
    application.run_polling()

if __name__ == '__main__':
    main()  (как можно улучшить данный код ?)