"""
core/rate_limiter.py — прогрессивный rate limiter для парсинга.
Портировано из DarkParserBot с адаптацией.
"""
import asyncio
import random
import time


class RateLimiter:
    """
    Прогрессивные задержки между запросами:
    - Базовая случайная задержка min_delay..max_delay
    - Каждые progressive_step запросов увеличивается на progressive_add
    - Каждые long_pause_every запросов — длинная пауза (имитация человека)
    """

    def __init__(
        self,
        min_delay: float = 2.0,
        max_delay: float = 4.0,
        progressive_step: int = 10,
        progressive_add: float = 0.5,
        progressive_max: float = 8.0,
        long_pause_every: int = 25,
        long_pause_sec: float = 10.0,
    ):
        self.min_delay = min_delay
        self.max_delay = max_delay
        self.progressive_step = progressive_step
        self.progressive_add = progressive_add
        self.progressive_max = progressive_max
        self.long_pause_every = long_pause_every
        self.long_pause_sec = long_pause_sec
        self._call_count = 0

    def reset(self):
        self._call_count = 0

    def _current_delay(self) -> float:
        base = random.uniform(self.min_delay, self.max_delay)
        if self.progressive_step > 0 and self._call_count > 0:
            steps = self._call_count // self.progressive_step
            extra = min(steps * self.progressive_add, self.progressive_max - base)
            if extra > 0:
                base += extra
        return base

    async def wait(self, stop_event=None):
        """Ждёт нужную задержку. Проверяет stop_event для прерывания."""
        self._call_count += 1

        # Длинная пауза каждые N запросов
        if self.long_pause_every > 0 and self._call_count % self.long_pause_every == 0:
            pause = self.long_pause_sec + random.uniform(0, 3.0)
            if stop_event:
                try:
                    await asyncio.wait_for(stop_event.wait(), timeout=pause)
                    return  # stop requested
                except asyncio.TimeoutError:
                    pass
            else:
                await asyncio.sleep(pause)

        # Обычная задержка
        delay = self._current_delay()
        if stop_event:
            try:
                await asyncio.wait_for(stop_event.wait(), timeout=delay)
            except asyncio.TimeoutError:
                pass
        else:
            await asyncio.sleep(delay)

    async def on_flood(self, wait_seconds: int):
        """Вызывается при FloodWaitError — ждёт + увеличивает счётчик.

        Капаем 1800с (30 мин). Telegram изредка возвращает 86400 (сутки) — на
        неё нет смысла ждать в job-loop'е, акк должен пометиться как
        приостановленный, юзер увидит причину и решит что делать."""
        await asyncio.sleep(min(int(wait_seconds), 1800) + 2)
        self._call_count += 5  # Ускоряем прогрессию после флуда
