"""
Работа с переменными бота
"""

import time
from typing import Any, Protocol
from ui_router.schema import VariableScope


class VariableRepository(Protocol):
    """
    Абстрактный репозиторий для работы с переменными.

    Реализуется пользователем для своего storage (PostgreSQL, Redis, etc.)

    Note:
        Protocol использует структурную типизацию (duck typing).
        Декоратор @abstractmethod не нужен для Protocol методов.
    """

    async def get(
        self,
        bot_id: int | str,
        name: str,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> Any:
        """
        Получить значение переменной

        Args:
            bot_id: ID бота
            name: Имя переменной
            scope: Область видимости (user, bot, chat)
            user_id: ID пользователя (для scope=user)
            chat_id: ID чата (для scope=chat)

        Returns:
            Значение переменной или None если не найдена
        """
        ...

    async def set(
        self,
        bot_id: int | str,
        name: str,
        value: Any,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> None:
        """
        Установить значение переменной

        Args:
            bot_id: ID бота
            name: Имя переменной
            value: Новое значение
            scope: Область видимости
            user_id: ID пользователя (для scope=user)
            chat_id: ID чата (для scope=chat)
        """
        ...

    async def delete(
        self,
        bot_id: int | str,
        name: str,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> None:
        """Удалить переменную"""
        ...


class InMemoryVariableRepository:
    """
    Простая in-memory реализация для разработки/тестирования.

    В продакшене используйте свою реализацию с БД.

    Note:
        Поддерживает TTL для переменных через asyncio tasks.
        При установке значения с ttl, создается задача для автоматического удаления.
    """

    def __init__(self) -> None:
        self._storage: dict[str, Any] = {}
        self._expiry: dict[str, float] = {}

    def _make_key(
        self,
        bot_id: int | str,
        name: str,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> str:
        """Создать уникальный ключ для переменной"""
        parts = [str(bot_id), scope.value, name]
        if scope == VariableScope.USER and user_id is not None:
            parts.append(f"user_{user_id}")
        if scope == VariableScope.CHAT and chat_id is not None:
            parts.append(f"chat_{chat_id}")
        return ":".join(parts)

    async def get(
        self,
        bot_id: int | str,
        name: str,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> Any:
        key = self._make_key(bot_id, name, scope, user_id, chat_id)

        if key in self._expiry and time.time() >= self._expiry[key]:
            self._storage.pop(key, None)
            self._expiry.pop(key, None)
            return None
        return self._storage.get(key)

    async def set(
        self,
        bot_id: int | str,
        name: str,
        value: Any,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
        ttl: int | None = None,
    ) -> None:
        """
        Установить значение переменной

        Args:
            bot_id: ID бота
            name: Имя переменной
            value: Новое значение
            scope: Область видимости
            user_id: ID пользователя (для scope=user)
            chat_id: ID чата (для scope=chat)
            ttl: Time to live в секундах (опционально)
        """
        key = self._make_key(bot_id, name, scope, user_id, chat_id)
        self._storage[key] = value

        if ttl is not None and ttl > 0:
            self._expiry[key] = time.time() + ttl
        else:
            self._expiry.pop(key, None)

    async def delete(
        self,
        bot_id: int | str,
        name: str,
        scope: VariableScope,
        user_id: int | None = None,
        chat_id: int | None = None,
    ) -> None:
        key = self._make_key(bot_id, name, scope, user_id, chat_id)
        self._storage.pop(key, None)
        self._expiry.pop(key, None)
