# Python 3.10.14
# Что сделать в будущем:
# - Переписать все кнопки под FSM, убрать из кнопок ссылки на файл
# - Переработать server_side_tasks под более процедурное задание аргументов ffmpeg (см. shutter encoder)
import asyncio
import logging
import sys
import os
import urllib.request
import random
import shutil
import re
from aiogram import Bot, Dispatcher, F
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart, Command, StateFilter
from aiogram.types import Message, CallbackQuery
from aiogram.client.default import DefaultBotProperties
from aiogram.utils.markdown import hbold
from aiogram.types import FSInputFile
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import StatesGroup, State
from aiogram.client.session.aiohttp import AiohttpSession

from aiogram.filters.chat_member_updated import (
    ChatMemberUpdatedFilter,
    IS_NOT_MEMBER,
    MEMBER,
)
from aiogram.types import ChatMemberUpdated

from aiogram.client.telegram import TelegramAPIServer

from server_side_tasks import (
    ffmpeg_logo_writer,
    runWithCommandLine,
    ffmpeg_video_cutter,
)
from buttons import Default, create_channel_buttons, create_playlist_buttons
from vk_video import get_user_groups, get_video_albums, upload_video_to_vk, video_iframe

# Запись токена в env
os.environ["VIDEO_EDITOR_TG_TOKEN"] = "7820159565:AAHialR9pX1vxvLUyHuU3Nljr_5FUSF0ovU"
# Токен VK API (нужно установить)
os.environ["VK_ACCESS_TOKEN"] = (
    "vk1.a.61Oa7Yjqayl6unaISLtVdstR8HEl2qvOYyFikZoMvUjk0TsLa0QreBRTZGbF2d75wi5vk6Ht9Qn3zdS9EjK0SxAinxSmH_ddIF_2y2W8Ic-OWKdJvFYW_bhMM9jfbErCaucrWcOYAqHnu9mneXE1JgTZT7W97gOaCEzI--sYq85tWbYXOD0Tzf3z1lwnV6GvBwCYe-jfTaIvnd5av0HstA"
)
TOKEN = os.environ["VIDEO_EDITOR_TG_TOKEN"]
LOCAL = True
# Получаем хост API из переменных окружения или используем localhost по умолчанию
TELEGRAM_API_HOST = os.environ.get("TELEGRAM_API_HOST", "localhost")
TELEGRAM_API_PORT = os.environ.get("TELEGRAM_API_PORT", "8081")
LOCAL_HOST = f"http://{TELEGRAM_API_HOST}:{TELEGRAM_API_PORT}"
WATERMARK_BUTTON = "2"
TRIM_BUTTON = "3"
# FORWARD_CHAT_ID=-1002470616678
FORWARD_CHAT_ID = -1002391500870

# Разрешенные сообщества VK для загрузки видео
ALLOWED_VK_OWNER_IDS = [-23419392, -88252733]  # @klops39 и @stranakaliningrad

dp = Dispatcher()


class Editing(StatesGroup):
    SubsState = State()
    VideoEditState = State()
    AudioEditState = State()

    GettingWatermarkState = State()
    EmbedSubsState = State()
    ComplexEditState = State()

    # Состояния для загрузки в VK Video
    VKChannelSelectState = State()
    VKPlaylistSelectState = State()
    VKTitleState = State()
    VKDescriptionState = State()


match_dict = {"quicktime": "mov", "mp4": "mp4"}


def extract_video_name_and_ext(video) -> tuple[str, str]:
    video_name = video.file_name
    if video_name is not None:
        video_name_parts = video_name.split(".")
        ext = video_name_parts[-1]
        video_name = video_name_parts[0]
    else:
        video_name = "temp"
        ext = video.mime_type.split("/")[-1]
        ext = match_dict.get(ext, "mp4")
    return video_name, ext


def get_db_file_id_from_message(message_text: str | None) -> str | None:
    if not message_text:
        return None
    try:
        return message_text.split("#")[-1]
    except (AttributeError, IndexError):
        return None


def get_video_path(user_id: int, db_file_id: str, video_name: str, ext: str) -> str:
    return os.path.abspath(
        f"workdir{os.sep}{user_id}{os.sep}{db_file_id}{os.sep}{video_name}.{ext}"
    )


async def safe_delete(bot: Bot, chat_id: int, msg_id: int | list[int]) -> None:
    try:
        if isinstance(msg_id, list):
            if msg_id:
                await bot.delete_messages(chat_id=chat_id, message_ids=msg_id)
        else:
            await bot.delete_message(chat_id=chat_id, message_id=msg_id)
    except Exception:
        pass


def extract_video_data_from_callback(
    callback_query: CallbackQuery,
) -> tuple[str, dict] | None:
    msg = callback_query.message
    db_file_id = get_db_file_id_from_message(msg.text)
    if not db_file_id or not msg.reply_to_message or not msg.reply_to_message.video:
        return None
    video = msg.reply_to_message.video
    video_name, ext = extract_video_name_and_ext(video)
    return db_file_id, {
        "video": [db_file_id, video_name, ext, video.width, video.height],
        "videoMes": msg.reply_to_message,
    }


def find_video_file(
    user_id: int, db_file_id: str, video_name: str, ext: str
) -> str | None:
    dir_path = os.path.dirname(get_video_path(user_id, db_file_id, video_name, ext))
    if not os.path.exists(dir_path):
        return None
    for file in os.listdir(dir_path):
        if file.endswith(("_edited.mp4", "_edited.mov", f"_edited.{ext}")):
            path = os.path.join(dir_path, file)
            if os.path.exists(path):
                return path
    for path in [
        os.path.join(dir_path, f"{video_name}_edited.{ext}"),
        get_video_path(user_id, db_file_id, video_name, ext),
    ]:
        if os.path.exists(path):
            return path
    return None


async def send_processed_video(
    bot: Bot,
    user_id: int,
    out_path: str,
    db_file_id: str,
    state: FSMContext,
    video_data: dict | None = None,
) -> None:
    if not os.path.exists(out_path) or os.path.getsize(out_path) == 0:
        await bot.send_message(user_id, f"❌ Ошибка: выходной файл не найден или пустой. Путь: {out_path}")
        return
    await state.update_data({
        "out_path": out_path,
        "video": video_data["video"] if video_data else None,
        "videoMes": video_data.get("videoMes") if video_data else None,
    })
    msg = await bot.send_video(
        user_id, FSInputFile(out_path),
        caption=f"Ваше видео готово!\n\nУникальный номер: #{db_file_id}",
        reply_markup=Default.send_processed_to_vk_markup,
    )
    await bot.forward_message(chat_id=FORWARD_CHAT_ID, from_chat_id=msg.chat.id, message_id=msg.message_id)


# Загрузчик
def download_file(url, path):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    res = urllib.request.urlretrieve(url, path)
    return res


async def download_video(message: Message, db_file_id: int) -> str:
    user_id = message.from_user.id

    file_id = message.video.file_id
    video_name, ext = extract_video_name_and_ext(message.video)
    file_info = await message.bot.get_file(file_id)
    file_path = file_info.file_path
    # tokenTG = os.environ['VIDEO_EDITOR_TG_TOKEN']
    if LOCAL:
        file_path = os.path.abspath(file_path)
        file_saved_path = get_video_path(user_id, db_file_id, video_name, ext)
        directory = os.path.split(file_saved_path)[0]
        if not os.path.exists(directory):
            os.makedirs(directory, exist_ok=True)
        shutil.move(file_path, file_saved_path)
    else:
        download_url = f"https://api.telegram.org/file/bot{TOKEN}/{file_path}"
        file_saved_path = get_video_path(user_id, db_file_id, video_name, ext)
        download_file(download_url, file_saved_path)

    return file_path


@dp.message(CommandStart(ignore_case=True))
async def command_start_handler(message: Message) -> None:
    await message.answer(
        f"Привет, {hbold(message.from_user.full_name)}!\nЯ обработчик видео и аудио. Отправь мне видео, и приступим к обработке"
    )


# Проверка работоспособности бота
@dp.message(F.text.lower() == "ping")
async def ping_handler(message: Message) -> None:
    await message.answer(
        f"Да работаю я, {hbold(message.from_user.first_name)}!\nЗа что ты меня пинаешь?"
    )


# Обработчик видео для состояний VK - очищает state и начинает заново
@dp.message(
    F.video,
    StateFilter(
        Editing.VKChannelSelectState,
        Editing.VKPlaylistSelectState,
        Editing.VKTitleState,
        Editing.VKDescriptionState,
    ),
)
async def video_during_vk_upload(message: Message, state: FSMContext):
    """Обрабатывает отправку видео во время процесса загрузки в VK"""
    # Очищаем старое состояние
    await state.clear()

    # Начинаем работу с новым видео
    db_file_id = random.randint(0, 10**15)
    await message.reply(
        reply_markup=Default.default_markup,
        text=f"Видео готово к работе! Используйте кнопки под сообщением, чтобы редактировать его! \n\n Уникальный номер: #{db_file_id}\n\n(Предыдущая операция загрузки в VK была отменена)",
    )


# Начало работы с видео. Получаем видео, ставим на него кнопки
@dp.message(F.video, StateFilter(None))
async def display_default(message: Message):
    db_file_id = random.randint(0, 10**15)
    await message.reply(
        reply_markup=Default.default_markup,
        text=f"Видео готово к работе! Используйте кнопки под сообщением, чтобы редактировать его! \n\n Уникальный номер: #{db_file_id}",
    )


# Обработка видео при нажатии на любую из кнопок постановки лого
@dp.callback_query(StateFilter(None), F.data.split(":")[0] == WATERMARK_BUTTON)
async def watermark_video(callback_query: CallbackQuery, bot: Bot, state: FSMContext):
    # Извлекаем данные о видео
    result = extract_video_data_from_callback(callback_query)
    if not result:
        await callback_query.answer(
            "Ошибка: не найден ID файла или видео", show_alert=True
        )
        return

    db_file_id, video_data = result
    user_id = callback_query.from_user.id

    # Добавляем информацию о watermark
    data = video_data.copy()
    data["watermark"] = callback_query.data.split(":")[1]

    # Проверяем, нужно ли скачать видео
    video_name, ext = data["video"][1], data["video"][2]
    saved_path = get_video_path(user_id, db_file_id, video_name, ext)
    if not os.path.exists(saved_path):
        await download_video(video_data["videoMes"], db_file_id=db_file_id)

    processing_msg = await bot.send_message(
        callback_query.from_user.id, "⏳ Видео обрабатывается, пожалуйста, подождите..."
    )
    args = ffmpeg_logo_writer(user_id, data)
    runWithCommandLine(args)
    out_path = args[-1]
    await safe_delete(bot, callback_query.from_user.id, processing_msg.message_id)
    await send_processed_video(bot, user_id, out_path, db_file_id, state, video_data)


# <============================================ ПОДРЕЗКА ВИДЕО ========================================>
# Обработка подрезки видео
@dp.callback_query(StateFilter(None), F.data == TRIM_BUTTON)
async def trimVideo(callback_query: CallbackQuery, state: FSMContext, bot: Bot):
    await state.set_state(Editing.ComplexEditState)

    # Извлекаем данные о видео
    result = extract_video_data_from_callback(callback_query)
    if not result:
        await callback_query.answer(
            "Ошибка: не найден ID файла или видео", show_alert=True
        )
        return

    db_file_id, video_data = result
    data = video_data.copy()

    message = await bot.send_message(
        callback_query.from_user.id,
        text="Пожалуйста, укажите тайминги подрезки, разделяя сгменты переносом строки.\n\nПример: \n12:13-13:08\n15:00-16:59",
        reply_markup=Default.cancel_markup,
    )
    data["messages"] = [message]
    await state.set_data(data=data)


# Главная функция подрезки видео
@dp.message(Editing.ComplexEditState, F.text)
async def check_timings(message: Message, state: FSMContext):
    data = await state.get_data()
    user_id = message.from_user.id
    pattern = r"\d\d:\d\d-\d\d:\d\d"
    data["messages"].append(message)
    # Здесь блок проверки, корректно ли заданы тайминги
    result = re.match(pattern=pattern, string=message.text)
    if result is None:
        message_todel = await message.answer(
            text="Не найдено ничего похожего на тайминг. Пожалуйста, введите их корректно"
        )
        data["messages"].append(message_todel)
        await state.update_data(data=data)
        return
    matches = re.finditer(pattern, message.text, re.MULTILINE)
    timings = str()
    for matchNum, match in enumerate(matches, start=1):
        timings += match.group() + "\n"
    saved_path = get_video_path(
        user_id, data["video"][0], data["video"][1], data["video"][2]
    )
    if not os.path.exists(saved_path):
        await download_video(data["videoMes"], data["video"][0])
    cut = ffmpeg_video_cutter(user_id=user_id, data=data, timings=timings.strip())
    data["video"][1] = os.path.basename(cut).split(".")[0]

    message_todel = await message.answer(
        text="Какой логотип на него поставить?",
        reply_markup=Default.trim_watermark_markup,
    )
    data["messages"].append(message_todel)
    await state.update_data(data=data)
    return


@dp.callback_query(Editing.ComplexEditState, F.data.split(":")[0] == WATERMARK_BUTTON)
async def apply_watermark_to_trim(
    callback_query: CallbackQuery, state: FSMContext, bot: Bot
):
    data = await state.get_data()
    user_id = callback_query.from_user.id
    data["watermark"] = callback_query.data.split(":")[1]

    # Отправляем сообщение о начале обработки
    processing_msg = await bot.send_message(
        callback_query.from_user.id, "⏳ Видео обрабатывается, пожалуйста, подождите..."
    )

    args = ffmpeg_logo_writer(user_id, data)
    runWithCommandLine(args)
    out_path = args[-1]
    await safe_delete(bot, callback_query.from_user.id, processing_msg.message_id)
    db_file_id = data["video"][0]
    if "messages" in data:
        await safe_delete(
            bot,
            callback_query.from_user.id,
            [msg.message_id for msg in data["messages"] if hasattr(msg, "message_id")],
        )

    # Подготавливаем данные для сохранения в state
    video_data = {"video": data["video"], "videoMes": data.get("videoMes")}

    # Очищаем state и отправляем обработанное видео
    await state.clear()
    await send_processed_video(bot, user_id, out_path, db_file_id, state, video_data)

    await callback_query.answer("Видео готово!")


@dp.callback_query(Editing.ComplexEditState, F.data == "cancel")
async def cancel(callback_query: CallbackQuery, state: FSMContext, bot: Bot):
    data = await state.get_data()
    await safe_delete(
        bot,
        callback_query.from_user.id,
        [msg.message_id for msg in data.get("messages", [])],
    )
    await state.clear()
    await callback_query.answer("Операция отменена")


# Обработчик кнопки "Назад" (callback_data='9')
@dp.callback_query(F.data == "9")
async def back_button_handler(
    callback_query: CallbackQuery, state: FSMContext, bot: Bot
):
    """Обработчик кнопки 'Назад' - возвращает к главному меню"""
    # Очищаем состояние, если оно было установлено
    current_state = await state.get_state()

    # Если находимся в любом состоянии VK, очищаем state и удаляем сообщение
    # get_state() возвращает строку вида "Editing:VKChannelSelectState"
    if str(current_state) in [
        str(Editing.VKChannelSelectState),
        str(Editing.VKPlaylistSelectState),
        str(Editing.VKTitleState),
        str(Editing.VKDescriptionState),
    ]:
        await state.clear()
        await safe_delete(
            bot, callback_query.from_user.id, callback_query.message.message_id
        )
        await callback_query.answer("Возврат в главное меню")
        return

    if current_state:
        await state.clear()

    # Редактируем сообщение, возвращая главное меню
    try:
        await callback_query.message.edit_reply_markup(
            reply_markup=Default.default_markup
        )
        await callback_query.answer("Возврат в главное меню")
    except Exception:
        # Если не удалось отредактировать сообщение (например, оно уже было изменено),
        # отправляем новое сообщение с главным меню
        await callback_query.message.answer(
            "Главное меню:", reply_markup=Default.default_markup
        )
        await callback_query.answer("Возврат в главное меню")


# =================================================== Обработчики VK Video ================================================


# Вспомогательная функция для проверки токена и получения групп
async def get_vk_groups_and_markup():
    """Получает список групп VK и создает разметку кнопок"""
    vk_token = os.environ.get("VK_ACCESS_TOKEN")
    if not vk_token:
        raise ValueError(
            "VK токен не установлен. Установите переменную окружения VK_ACCESS_TOKEN"
        )

    groups = await get_user_groups(vk_token)

    if not groups:
        raise ValueError("У вас нет доступных групп для загрузки видео")

    # Фильтруем группы, оставляя только разрешенные
    allowed_groups = [g for g in groups if g["owner_id"] in ALLOWED_VK_OWNER_IDS]

    if not allowed_groups:
        raise ValueError("Нет доступных разрешенных групп для загрузки видео")

    markup = create_channel_buttons(allowed_groups)
    return markup


# Обработчик кнопки "Отправить в VK Video"
@dp.callback_query(F.data == "vk_upload")
async def start_vk_upload(callback_query: CallbackQuery, state: FSMContext):
    """Начинает процесс загрузки видео в VK Video - переход в состояние выбора канала"""
    try:
        # Сначала проверяем, есть ли сохраненный out_path в state
        data = await state.get_data()

        if "out_path" in data and os.path.exists(data["out_path"]) and "video" in data:
            # Используем сохраненные данные из state
            video_data = {
                "video": data["video"],
                "videoMes": data.get("videoMes"),
                "out_path": data["out_path"],
            }
        else:
            # Старая логика - извлекаем из сообщения
            querry_message = callback_query.message

            # Пытаемся получить db_file_id из текста сообщения
            db_file_id = get_db_file_id_from_message(querry_message.text)

            # Проверяем, есть ли видео в текущем сообщении (обработанное видео) или в reply_to_message
            video = None
            video_message = None

            if querry_message.video:
                # Видео в текущем сообщении (обработанное)
                video = querry_message.video
                video_message = querry_message
            elif (
                querry_message.reply_to_message
                and querry_message.reply_to_message.video
            ):
                # Видео в reply_to_message (исходное)
                video = querry_message.reply_to_message.video
                video_message = querry_message.reply_to_message

            if not video:
                await callback_query.answer(
                    "Ошибка: не найдено видео в сообщении", show_alert=True
                )
                return

            video_name, ext = extract_video_name_and_ext(video)
            video_width, video_height = video.width, video.height

            # Если db_file_id не найден в тексте, генерируем новый
            if not db_file_id:
                db_file_id = random.randint(0, 10**15)

            # Сохраняем данные о видео в состояние FSM
            video_data = {
                "video": [db_file_id, video_name, ext, video_width, video_height],
                "videoMes": video_message,  # Сохраняем ссылку на сообщение с видео для скачивания
            }

        await state.update_data(video_data)

        markup = await get_vk_groups_and_markup()

        await state.set_state(Editing.VKChannelSelectState)
        await callback_query.message.answer(
            "Выберите канал (группу) для загрузки видео:", reply_markup=markup
        )
        await callback_query.answer()
    except ValueError as e:
        await state.clear()
        await callback_query.answer(str(e), show_alert=True)
    except Exception as e:
        await state.clear()
        await callback_query.answer(
            f"Ошибка при получении списка групп: {str(e)}", show_alert=True
        )


# Обработчик выбора канала
@dp.callback_query(
    F.data.startswith("vk_channel_"), StateFilter(Editing.VKChannelSelectState)
)
async def select_channel(callback_query: CallbackQuery, state: FSMContext):
    """Обрабатывает выбор канала и переходит к выбору плейлиста"""
    try:
        owner_id = int(callback_query.data.split("_")[-1])

        # Проверяем, что выбранный канал разрешен
        if owner_id not in ALLOWED_VK_OWNER_IDS:
            await callback_query.answer(
                "Этот канал не разрешен для загрузки", show_alert=True
            )
            return

        vk_token = os.environ.get("VK_ACCESS_TOKEN")
        if not vk_token:
            await callback_query.answer(
                "Ошибка: VK токен не установлен", show_alert=True
            )
            return

        # Получаем список альбомов
        albums = await get_video_albums(vk_token, owner_id)

        # Сохраняем owner_id в state
        await state.update_data({"vk_owner_id": owner_id})

        # Создаем кнопки для выбора плейлиста
        markup = create_playlist_buttons(albums)

        await state.set_state(Editing.VKPlaylistSelectState)
        await callback_query.message.edit_text(
            "Выберите плейлист (альбом) для загрузки видео:", reply_markup=markup
        )
        await callback_query.answer()
    except Exception as e:
        await state.clear()
        await callback_query.answer(
            f"Ошибка при получении списка альбомов: {str(e)}", show_alert=True
        )


# Обработчик возврата к выбору канала
@dp.callback_query(
    F.data == "vk_back_to_channels", StateFilter(Editing.VKPlaylistSelectState)
)
async def back_to_channels(callback_query: CallbackQuery, state: FSMContext):
    """Возврат к выбору канала"""
    try:
        markup = await get_vk_groups_and_markup()

        await state.set_state(Editing.VKChannelSelectState)
        await callback_query.message.edit_text(
            "Выберите канал (группу) для загрузки видео:", reply_markup=markup
        )
        await callback_query.answer()
    except ValueError as e:
        await state.clear()
        await callback_query.answer(str(e), show_alert=True)
    except Exception as e:
        await state.clear()
        await callback_query.answer(f"Ошибка: {str(e)}", show_alert=True)


# Обработчик выбора плейлиста
@dp.callback_query(
    F.data.startswith("vk_playlist_"), StateFilter(Editing.VKPlaylistSelectState)
)
async def select_playlist(callback_query: CallbackQuery, state: FSMContext):
    """Обрабатывает выбор плейлиста и переходит к вводу названия"""
    album_id = int(callback_query.data.split("_")[-1])

    # Сохраняем album_id в state
    await state.update_data({"vk_album_id": album_id})

    await state.set_state(Editing.VKTitleState)
    await callback_query.message.edit_text("Введите название видео:")
    await callback_query.answer()


# Обработчик ввода названия
@dp.message(StateFilter(Editing.VKTitleState), F.text)
async def get_video_title(message: Message, state: FSMContext):
    """Обрабатывает ввод названия видео и переходит к вводу описания"""
    title = message.text.strip()

    if not title:
        await message.answer(
            "Название не может быть пустым. Пожалуйста, введите название:"
        )
        return

    # Сохраняем title в state
    await state.update_data({"vk_title": title})

    await state.set_state(Editing.VKDescriptionState)
    await message.answer("Введите описание видео (или отправьте /skip для пропуска):")


# Обработчик команды /cancel для состояний VK
@dp.message(
    Command("cancel"),
    StateFilter(
        Editing.VKTitleState,
        Editing.VKDescriptionState,
        Editing.VKChannelSelectState,
        Editing.VKPlaylistSelectState,
    ),
)
async def cancel_vk_upload(message: Message, state: FSMContext):
    """Отменяет процесс загрузки в VK"""
    await state.clear()
    await message.answer(
        "Загрузка в VK Video отменена.", reply_markup=Default.default_markup
    )


# Обработчик кнопки отмены для состояний VK
@dp.callback_query(
    F.data == "cancel",
    StateFilter(
        Editing.VKChannelSelectState,
        Editing.VKPlaylistSelectState,
        Editing.VKTitleState,
        Editing.VKDescriptionState,
    ),
)
async def cancel_vk_upload_callback(
    callback_query: CallbackQuery, state: FSMContext, bot: Bot
):
    """Отменяет процесс загрузки в VK через кнопку"""
    await state.clear()
    await callback_query.answer("Загрузка в VK Video отменена.")
    await safe_delete(
        bot, callback_query.from_user.id, callback_query.message.message_id
    )


# Обработчик для необработанных сообщений в состоянии VKTitleState
@dp.message(StateFilter(Editing.VKTitleState))
async def vk_title_invalid(message: Message):
    """Обрабатывает некорректные сообщения в состоянии ввода названия"""
    await message.answer("Пожалуйста, отправьте текстовое название видео.")


# Обработчик ввода описания
@dp.message(StateFilter(Editing.VKDescriptionState), F.text)
async def get_video_description(message: Message, state: FSMContext, bot: Bot):
    """Обрабатывает ввод описания и запускает загрузку видео"""
    description = message.text.strip()

    # Если пользователь отправил /skip, оставляем описание пустым
    if description.lower() == "/skip":
        description = ""

    # Сохраняем description в state
    await state.update_data({"vk_description": description})

    # Получаем все данные из state
    data = await state.get_data()

    # Проверяем наличие всех необходимых данных
    required_keys = ["video", "vk_owner_id", "vk_album_id", "vk_title"]
    missing_keys = [key for key in required_keys if key not in data]
    if missing_keys:
        await message.answer(
            f"Ошибка: отсутствуют необходимые данные: {', '.join(missing_keys)}"
        )
        await state.clear()
        return

    # Проверяем, что owner_id разрешен (дополнительная проверка безопасности)
    if data["vk_owner_id"] not in ALLOWED_VK_OWNER_IDS:
        await message.answer("Ошибка: выбранный канал не разрешен для загрузки")
        await state.clear()
        return

    # Определяем путь к видео
    user_id = message.from_user.id
    try:
        db_file_id, video_name, ext, *_ = data["video"]
    except (ValueError, TypeError):
        await message.answer("Ошибка: некорректные данные о видео")
        await state.clear()
        return

    # Сначала проверяем сохраненный out_path из state
    video_path = None
    if "out_path" in data and os.path.exists(data["out_path"]):
        video_path = data["out_path"]

    # Если out_path не найден, используем функцию поиска
    if not video_path:
        video_path = find_video_file(user_id, db_file_id, video_name, ext)

    # Если файл не найден, пытаемся скачать его из Telegram
    if not video_path:
        if "videoMes" in data and data["videoMes"]:
            try:
                await download_video(data["videoMes"], db_file_id)
                # После скачивания ищем файл снова
                video_path = find_video_file(user_id, db_file_id, video_name, ext)
            except Exception as e:
                await message.answer(f"Ошибка при скачивании видео: {str(e)}")
                await state.clear()
                return
        else:
            await message.answer(
                "Ошибка: файл видео не найден и нет возможности его скачать"
            )
            await state.clear()
            return

    # Финальная проверка существования файла
    if not video_path or not os.path.exists(video_path):
        await message.answer("Ошибка: файл видео не найден")
        await state.clear()
        return

    # Проверяем размер файла и информируем пользователя
    file_size = os.path.getsize(video_path)
    file_size_mb = file_size / (1024 * 1024)

    # Отправляем сообщение о начале загрузки
    loading_msg = await message.answer(
        f"Начинаю загрузку видео в VK Video...\n"
        f"Размер файла: {file_size_mb:.2f} MB\n"
        f"Это может занять некоторое время..."
    )

    try:
        vk_token = os.environ.get("VK_ACCESS_TOKEN")
        if not vk_token:
            await loading_msg.edit_text("Ошибка: VK токен не установлен")
            await state.clear()
            return

        # Обновляем сообщение о прогрессе
        await loading_msg.edit_text(
            f"Загружаю видео в VK Video...\n"
            f"Размер: {file_size_mb:.2f} MB\n"
            f"Пожалуйста, подождите..."
        )

        # Загружаем видео (асинхронная операция)
        video_url = await upload_video_to_vk(
            access_token=vk_token,
            video_path=video_path,
            owner_id=data["vk_owner_id"],
            album_id=data["vk_album_id"],
            title=data["vk_title"],
            description=data["vk_description"],
        )

        # Формируем iframe
        iframe_code = video_iframe(video_url)

        # Отправляем сообщение об успехе с ссылкой
        await loading_msg.edit_text(
            f"✅ Видео успешно загружено в VK Video!\n\nСсылка на видео: {video_url}"
        )

        # Отправляем код iframe отдельным сообщением без parse_mode
        # чтобы избежать проблем с парсингом HTML/Markdown
        await message.answer(
            f"Код для встраивания:\n\n{iframe_code}",
            parse_mode=None,  # Явно отключаем parse_mode, чтобы iframe не парсился
        )

        # Очищаем состояние после успешной загрузки
        await state.clear()

    except ValueError as e:
        # Ошибки валидации (размер файла и т.д.)
        await loading_msg.edit_text(f"❌ Ошибка: {str(e)}")
        await state.clear()
    except FileNotFoundError as e:
        await loading_msg.edit_text(f"❌ Ошибка: {str(e)}")
        await state.clear()
    except Exception as e:
        error_msg = str(e)
        # Сокращаем длинные сообщения об ошибках
        if len(error_msg) > 500:
            error_msg = error_msg[:500] + "..."
        await loading_msg.edit_text(f"❌ Ошибка при загрузке видео: {error_msg}")
        await state.clear()


# Обработчик для необработанных сообщений в состоянии VKDescriptionState
@dp.message(StateFilter(Editing.VKDescriptionState))
async def vk_description_invalid(message: Message):
    """Обрабатывает некорректные сообщения в состоянии ввода описания"""
    await message.answer(
        "Пожалуйста, отправьте текстовое описание видео или /skip для пропуска."
    )


@dp.callback_query(StateFilter(None), F.data)
async def wip_button(callbac_query: CallbackQuery):
    await callbac_query.message.answer(
        text="Эта функция в разработке. Следите за обновлениями!"
    )


@dp.callback_query(F.data == "$startup_button_pressed")
async def data_handler(callback_query: CallbackQuery):
    print(type(callback_query))
    await callback_query.answer(
        text="Зачем ты нажал на кнопку?"  # Происходит алёрт вместо отправки сообщения
    )

    await callback_query.message.edit_reply_markup(
        reply_markup=None
    )  # чистим кнопку, чтобы на неё нельзя было больше нажать

    await callback_query.message.answer(
        text="Зачем ты нажал на кнопку?"  # отправляется сообщение
    )


@dp.my_chat_member(
    ChatMemberUpdatedFilter(member_status_changed=IS_NOT_MEMBER >> MEMBER)
)
async def joined_group(event: ChatMemberUpdated, bot: Bot):
    try:
        chat_info = await bot.get_chat(event.chat.id)
        if chat_info.permissions.can_send_messages:
            await event.answer(text="Я родился!")
    except Exception:
        print("passed")


async def main() -> None:
    # Ожидание готовности Telegram Bot API
    import aiohttp

    max_retries = 30
    retry_delay = 2

    # Проверяем доступность статистического порта (8082)
    stat_port = int(TELEGRAM_API_PORT) + 1 if TELEGRAM_API_HOST != "localhost" else 8082
    stat_url = f"http://{TELEGRAM_API_HOST}:{stat_port}"

    for i in range(max_retries):
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(
                    stat_url, timeout=aiohttp.ClientTimeout(total=2)
                ) as resp:
                    if resp.status in [
                        200,
                        404,
                    ]:  # 404 тоже нормально, главное что сервер отвечает
                        print(f"Telegram Bot API is ready at {LOCAL_HOST}")
                        break
        except Exception as e:
            if i < max_retries - 1:
                print(
                    f"Waiting for Telegram Bot API... (attempt {i + 1}/{max_retries})"
                )
                await asyncio.sleep(retry_delay)
            else:
                print(f"Failed to connect to Telegram Bot API at {LOCAL_HOST}: {e}")
                raise

    session = AiohttpSession(
        api=TelegramAPIServer.from_base(LOCAL_HOST, is_local=LOCAL)
    )
    bot = Bot(
        token=TOKEN,
        session=session,
        default=DefaultBotProperties(parse_mode=ParseMode.HTML),
    )
    await bot.delete_webhook(drop_pending_updates=True)
    await dp.start_polling(bot)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, stream=sys.stdout)
    asyncio.run(main())
