"""
Middleware for idempotency of callback queries (buttons).
Ensures each button can only be processed once.
"""
import logging
from typing import Callable, Awaitable, Any, Set
from aiogram import BaseMiddleware
from aiogram.types import CallbackQuery

logger = logging.getLogger(__name__)

# Global set to track processed callback query IDs
_processed_callbacks: Set[str] = set()


class IdempotencyMiddleware(BaseMiddleware):
    """
    Middleware that ensures callback queries are processed only once.
    Uses callback query ID to track processed callbacks.
    """
    
    async def __call__(
        self,
        handler: Callable[[CallbackQuery, dict], Awaitable[Any]],
        event: CallbackQuery,
        data: dict,
    ) -> Any:
        # Create unique key from user_id and callback query ID for better reliability
        user_id = event.from_user.id if event.from_user else None
        callback_id = event.id
        unique_key = f"{user_id}:{callback_id}" if user_id else str(callback_id)
        
        # Check if this callback was already processed
        if unique_key in _processed_callbacks:
            logger.debug(f"Callback {unique_key} already processed, skipping")
            # Answer silently to prevent Telegram from showing loading indicator
            try:
                await event.answer()
            except Exception:
                pass
            return
        
        # Mark as processed before handling
        _processed_callbacks.add(unique_key)
        
        # Clean up old entries periodically (keep last 1000)
        if len(_processed_callbacks) > 1000:
            # Remove oldest entries (simple cleanup, keep last 500)
            old_items = list(_processed_callbacks)[:-500]
            _processed_callbacks.difference_update(old_items)
        
        try:
            # Process the callback
            return await handler(event, data)
        except Exception as e:
            # If processing failed, remove from processed set to allow retry
            _processed_callbacks.discard(unique_key)
            logger.error(f"Error processing callback {unique_key}: {e}")
            raise

