"""
Утилиты для автоматического удаления сообщений
"""
from typing import Optional, Callable, Awaitable, Dict, Any
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from asyncio import sleep, create_task, Task, CancelledError

from aiogram import Bot
from aiogram.types import Message
from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError

from middleware.loggers import logger
from .format_time import format_duration

__all__ = (
    'auto_delete_message',
    'schedule_delete',
    'cancel_delete',
    'delete_after',
    'auto_delete_manager',
    'AutoDeleteManager',
    'DeleteTask',
    'delete_both_after',
    'delete_messages_after',
)


@dataclass
class DeleteTask:
    """
    Задача на удаление сообщения.

    Attributes:
        chat_id: ID чата
        message_id: ID сообщения
        delete_at: Время удаления
        task: Asyncio task
        created_at: Время создания задачи
        reason: Причина удаления
        callback: Callback функция после удаления
    """
    chat_id: int
    message_id: int
    delete_at: datetime
    task: Optional[Task] = None
    created_at: datetime = field(default_factory=datetime.now)
    reason: Optional[str] = None
    callback: Optional[Callable[[], Awaitable[None]]] = None

    @property
    def delay(self) -> int:
        """Задержка до удаления в секундах"""
        delta = self.delete_at - datetime.now()
        return max(0, int(delta.total_seconds()))

    @property
    def is_expired(self) -> bool:
        """Истекло ли время удаления"""
        return datetime.now() >= self.delete_at

    def __repr__(self) -> str:
        return (
            f"DeleteTask(chat={self.chat_id}, msg={self.message_id}, "
            f"delay={self.delay}s, reason={self.reason})"
        )


class AutoDeleteManager:
    """
    Менеджер автоматического удаления сообщений.

    Возможности:
    - Планирование удаления с задержкой
    - Отмена запланированного удаления
    - Массовое удаление
    - Callback функции
    - История задач
    - Автоматическая очистка завершенных задач

    Example:
        ```python
        from utils.auto_delete import auto_delete_manager

        # Планирование удаления
        await auto_delete_manager.schedule(
            bot=bot,
            chat_id=message.chat.id,
            message_id=message.message_id,
            delay=60,
            reason="Временное сообщение"
        )

        # Отмена удаления
        auto_delete_manager.cancel(message.chat.id, message.message_id)

        # Получение статистики
        stats = auto_delete_manager.get_stats()
        ```
    """

    def __init__(self):
        # Активные задачи: {(chat_id, message_id): DeleteTask}
        self.tasks: Dict[tuple[int, int], DeleteTask] = {}

        # Завершенные задачи (последние 100)
        self.completed: list[DeleteTask] = []
        self.max_completed = 100

        # Статистика
        self.total_scheduled: int = 0
        self.total_deleted: int = 0
        self.total_failed: int = 0
        self.total_cancelled: int = 0

    async def schedule(
            self,
            bot: Bot,
            chat_id: int,
            message_id: int,
            delay: int,
            reason: Optional[str] = None,
            callback: Optional[Callable[[], Awaitable[None]]] = None,
            log: bool = True
    ) -> DeleteTask:
        """
        Планирует удаление сообщения.

        Args:
            bot: Экземпляр бота
            chat_id: ID чата
            message_id: ID сообщения
            delay: Задержка в секундах
            reason: Причина удаления
            callback: Callback функция после удаления
            log: Логировать планирование

        Returns:
            DeleteTask: Созданная задача

        Example:
            >> task = await auto_delete_manager.schedule(
            ...     bot=bot,
            ...     chat_id=message.chat.id,
            ...     message_id=message.message_id,
            ...     delay=60,
            ...     reason="Спам"
            ... )
        """
        # Отменяем предыдущую задачу если есть
        key = (chat_id, message_id)
        if key in self.tasks:
            self.cancel(chat_id, message_id)

        # Создаем задачу
        delete_at = datetime.now() + timedelta(seconds=delay)
        task_data = DeleteTask(
            chat_id=chat_id,
            message_id=message_id,
            delete_at=delete_at,
            reason=reason,
            callback=callback
        )

        # Создаем asyncio task
        task = create_task(self._delete_task(bot, task_data, log))
        task_data.task = task

        # Сохраняем
        self.tasks[key] = task_data
        self.total_scheduled += 1

        if log:
            delay_str = format_duration(delay)
            logger.info(
                f"Запланировано удаление сообщения через {delay_str}",
                log_type='AUTO_DELETE'
            )

        return task_data

    async def _delete_task(
            self,
            bot: Bot,
            task_data: DeleteTask,
            log: bool
    ) -> None:
        """
        Внутренняя функция для выполнения задачи удаления.

        Args:
            bot: Экземпляр бота
            task_data: Данные задачи
            log: Логировать выполнение
        """
        key = (task_data.chat_id, task_data.message_id)

        try:
            # Ждем
            await sleep(task_data.delay)

            # Удаляем сообщение
            await bot.delete_message(
                chat_id=task_data.chat_id,
                message_id=task_data.message_id
            )

            self.total_deleted += 1

            if log:
                reason_str = f" (причина: {task_data.reason})" if task_data.reason else ""
                logger.info(
                    f"Сообщение удалено автоматически{reason_str}",
                    log_type='AUTO_DELETE'
                )

            # Вызываем callback если есть
            if task_data.callback:
                try:
                    await task_data.callback()
                except Exception as e:
                    logger.error(
                        f"Ошибка в callback автоудаления: {e}",
                        log_type='AUTO_DELETE'
                    )

        except CancelledError:
            # Задача отменена
            self.total_cancelled += 1

            if log:
                logger.debug(
                    f"Удаление сообщения отменено",
                    log_type='AUTO_DELETE'
                )
            raise

        except (TelegramBadRequest, TelegramForbiddenError) as e:
            # Ошибка удаления
            self.total_failed += 1

            if log:
                logger.warning(
                    f"Не удалось автоматически удалить сообщение: {e}",
                    log_type='AUTO_DELETE'
                )

        finally:
            # Удаляем из активных задач
            if key in self.tasks:
                completed_task = self.tasks.pop(key)

                # Сохраняем в завершенные
                self.completed.append(completed_task)
                if len(self.completed) > self.max_completed:
                    self.completed.pop(0)

    def cancel(
            self,
            chat_id: int,
            message_id: int,
            log: bool = True
    ) -> bool:
        """
        Отменяет запланированное удаление.

        Args:
            chat_id: ID чата
            message_id: ID сообщения
            log: Логировать отмену

        Returns:
            bool: True если задача была отменена

        Example:
            >> cancelled = auto_delete_manager.cancel(
            ...     chat_id=message.chat.id,
            ...     message_id=message.message_id
            ... )
        """
        key = (chat_id, message_id)

        if key in self.tasks:
            task_data = self.tasks[key]

            # Отменяем asyncio task
            if task_data.task and not task_data.task.done():
                task_data.task.cancel()

            # Удаляем из активных
            self.tasks.pop(key)

            if log:
                logger.debug(
                    f"Автоудаление отменено для сообщения {message_id}",
                    log_type='AUTO_DELETE'
                )

            return True

        return False

    def cancel_all(self, chat_id: Optional[int] = None) -> int:
        """
        Отменяет все запланированные удаления.

        Args:
            chat_id: ID чата (если None, отменяет для всех чатов)

        Returns:
            int: Количество отмененных задач

        Example:
            >> # Отменить для всех чатов
            >> count = auto_delete_manager.cancel_all()

            >> # Отменить для конкретного чата
            >> count = auto_delete_manager.cancel_all(chat_id=message.chat.id)
        """
        cancelled_count = 0

        # Собираем ключи для отмены
        keys_to_cancel = []
        for key, task_data in self.tasks.items():
            if chat_id is None or task_data.chat_id == chat_id:
                keys_to_cancel.append(key)

        # Отменяем
        for key in keys_to_cancel:
            if self.cancel(key[0], key[1], log=False):
                cancelled_count += 1

        if cancelled_count > 0:
            logger.info(
                f"Отменено {cancelled_count} задач автоудаления",
                log_type='AUTO_DELETE'
            )

        return cancelled_count

    def get_task(
            self,
            chat_id: int,
            message_id: int
    ) -> Optional[DeleteTask]:
        """
        Получает задачу по ID чата и сообщения.

        Args:
            chat_id: ID чата
            message_id: ID сообщения

        Returns:
            Optional[DeleteTask]: Задача или None
        """
        key = (chat_id, message_id)
        return self.tasks.get(key)

    def get_chat_tasks(self, chat_id: int) -> list[DeleteTask]:
        """
        Получает все задачи для чата.

        Args:
            chat_id: ID чата

        Returns:
            list[DeleteTask]: Список задач
        """
        return [
            task for task in self.tasks.values()
            if task.chat_id == chat_id
        ]

    def get_stats(self) -> Dict[str, Any]:
        """
        Возвращает статистику менеджера.

        Returns:
            Dict: Словарь со статистикой

        Example:
            >> stats = auto_delete_manager.get_stats()
            >> print(f"Активных задач: {stats['active_tasks']}")
        """
        return {
            'active_tasks': len(self.tasks),
            'completed_tasks': len(self.completed),
            'total_scheduled': self.total_scheduled,
            'total_deleted': self.total_deleted,
            'total_failed': self.total_failed,
            'total_cancelled': self.total_cancelled,
            'success_rate': (
                f"{(self.total_deleted / self.total_scheduled * 100):.1f}%"
                if self.total_scheduled > 0 else "0%"
            )
        }

    def cleanup_expired(self) -> int:
        """
        Удаляет истекшие задачи (которые должны были выполниться, но не выполнились).

        Returns:
            int: Количество удаленных задач
        """
        expired_keys = [
            key for key, task in self.tasks.items()
            if task.is_expired and (not task.task or task.task.done())
        ]

        for key in expired_keys:
            self.tasks.pop(key)

        return len(expired_keys)


# Глобальный менеджер
auto_delete_manager = AutoDeleteManager()


# ================= УДОБНЫЕ ФУНКЦИИ =================

async def auto_delete_message(
        bot: Bot,
        chat_id: int,
        message_id: int,
        delay: int = 604800,
        reason: Optional[str] = None
) -> DeleteTask:
    """
    Автоматически удаляет сообщение через указанное время.

    Args:
        bot: Экземпляр бота
        chat_id: ID чата
        message_id: ID сообщения
        delay: Задержка в секундах (по умолчанию 7 дней)
        reason: Причина удаления

    Returns:
        DeleteTask: Созданная задача

    Example:
        >> # Удалить через 1 минуту
        >> await auto_delete_message(bot, chat_id, message_id, delay=60)

        >> # Удалить через 7 дней (по умолчанию)
        >> await auto_delete_message(bot, chat_id, message_id)
    """
    return await auto_delete_manager.schedule(
        bot=bot,
        chat_id=chat_id,
        message_id=message_id,
        delay=delay,
        reason=reason
    )


async def schedule_delete(
        message: Message,
        delay: int,
        reason: Optional[str] = None
) -> DeleteTask:
    """
    Планирует удаление сообщения (упрощенная версия).

    Args:
        message: Объект сообщения
        delay: Задержка в секундах
        reason: Причина удаления

    Returns:
        DeleteTask: Созданная задача

    Example:
        >> # Планируем удаление через 30 секунд
        >> await schedule_delete(message, delay=30, reason="Временное")
    """
    return await auto_delete_manager.schedule(
        bot=message.bot,
        chat_id=message.chat.id,
        message_id=message.message_id,
        delay=delay,
        reason=reason
    )


def cancel_delete(message: Message) -> bool:
    """
    Отменяет запланированное удаление сообщения.

    Args:
        message: Объект сообщения

    Returns:
        bool: True если удаление было отменено

    Example:
        >> if cancel_delete(message):
        ...     await message.answer("Удаление отменено")
    """
    return auto_delete_manager.cancel(
        chat_id=message.chat.id,
        message_id=message.message_id
    )


async def delete_after(
        message: Message,
        text: str,
        delay: int = 10,
        **kwargs
) -> Message:
    """
    Отправляет сообщение и автоматически удаляет его через указанное время.

    Args:
        message: Исходное сообщение
        text: Текст нового сообщения
        delay: Задержка до удаления в секундах
        **kwargs: Дополнительные параметры для message.answer()

    Returns:
        Message: Отправленное сообщение

    Example:
        >> # Отправить и удалить через 10 секунд
        >> await delete_after(message, "Это временное сообщение")

        >> # Отправить и удалить через 5 секунд
        >> await delete_after(
        ...     message,
        ...     "⚠️ Ошибка!",
        ...     delay=5,
        ...     parse_mode="HTML"
        ... )
    """
    sent_message = await message.answer(text, **kwargs)

    await auto_delete_manager.schedule(
        bot=message.bot,
        chat_id=sent_message.chat.id,
        message_id=sent_message.message_id,
        delay=delay,
        reason="delete_after"
    )

    return sent_message


async def delete_both_after(
        original: Message,
        reply_text: str,
        delay: int = 10,
        **kwargs
) -> Message:
    """
    Отправляет ответ и удаляет оба сообщения через указанное время.

    Args:
        original: Исходное сообщение
        reply_text: Текст ответа
        delay: Задержка до удаления
        **kwargs: Дополнительные параметры

    Returns:
        Message: Отправленное сообщение

    Example:
        >> # Удалить и команду, и ответ через 5 секунд
        >> await delete_both_after(
        ...     message,
        ...     "✅ Команда выполнена",
        ...     delay=5
        ... )
    """
    # Отправляем ответ
    sent = await delete_after(original, reply_text, delay, **kwargs)

    # Планируем удаление оригинала
    await auto_delete_manager.schedule(
        bot=original.bot,
        chat_id=original.chat.id,
        message_id=original.message_id,
        delay=delay,
        reason="delete_both"
    )

    return sent


async def delete_messages_after(
        bot: Bot,
        chat_id: int,
        message_ids: list[int],
        delay: int
) -> int:
    """
    Планирует удаление нескольких сообщений.

    Args:
        bot: Экземпляр бота
        chat_id: ID чата
        message_ids: Список ID сообщений
        delay: Задержка до удаления

    Returns:
        int: Количество запланированных удалений

    Example:
        >> # Удалить все сообщения через 1 час
        >> count = await delete_messages_after(
        ...     bot,
        ...     chat_id,
        ...     [123, 124, 125, 126],
        ...     delay=3600
        ... )
    """
    count = 0

    for message_id in message_ids:
        await auto_delete_manager.schedule(
            bot=bot,
            chat_id=chat_id,
            message_id=message_id,
            delay=delay,
            reason="mass_delete",
            log=False
        )
        count += 1

    logger.info(
        f"Запланировано удаление {count} сообщений через {format_duration(delay)}",
        log_type='AUTO_DELETE'
    )

    return count
