from __future__ import annotations

import logging
import time
from typing import Optional

import redis

from .. import settings

log = logging.getLogger(__name__)

try:
    DEFAULT_RPM_LIMIT = max(1, int(getattr(settings, "GOOGLE_SHEETS_RPM_LIMIT", 15)))
except Exception:
    DEFAULT_RPM_LIMIT = 15
WINDOW_SECONDS = 60
KEY_PREFIX = "google_sheets_rpm"
KEY_TTL_SECONDS = 90

_redis_client: Optional[redis.Redis] = None

_TOKEN_BUCKET_SCRIPT = """
local key = KEYS[1]
local now_ms = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local refill_per_ms = tonumber(ARGV[3])
local ttl = tonumber(ARGV[4])

local data = redis.call('HMGET', key, 'tokens', 'ts')
local tokens = tonumber(data[1])
local last_ts = tonumber(data[2])

if not tokens or not last_ts then
  tokens = capacity - 1
  last_ts = now_ms
  redis.call('HMSET', key, 'tokens', tokens, 'ts', last_ts)
  redis.call('EXPIRE', key, ttl)
  return 1
end

local elapsed = math.max(0, now_ms - last_ts)
local refill = elapsed * refill_per_ms
tokens = math.min(capacity, tokens + refill)

if tokens < 1 then
  redis.call('HMSET', key, 'tokens', tokens, 'ts', now_ms)
  redis.call('EXPIRE', key, ttl)
  return 0
end

tokens = tokens - 1
redis.call('HMSET', key, 'tokens', tokens, 'ts', now_ms)
redis.call('EXPIRE', key, ttl)
return 1
"""


def _get_redis() -> Optional[redis.Redis]:
    global _redis_client
    if _redis_client is not None:
        return _redis_client
    redis_url = settings.REDIS_URL or "redis://127.0.0.1:6379/0"
    try:
        _redis_client = redis.from_url(redis_url, decode_responses=True)
        return _redis_client
    except Exception as exc:
        log.warning("Google rate limiter Redis init failed: %s", exc)
        return None


def acquire(account_id: str, limit: int = DEFAULT_RPM_LIMIT) -> bool:
    if not account_id:
        return True
    redis_conn = _get_redis()
    if not redis_conn:
        return True

    key = f"{KEY_PREFIX}:{account_id}"
    now_ms = int(time.time() * 1000)
    capacity = max(1, int(limit))
    refill_per_ms = capacity / float(WINDOW_SECONDS * 1000)
    ttl = max(WINDOW_SECONDS, KEY_TTL_SECONDS)

    try:
        allowed = redis_conn.eval(
            _TOKEN_BUCKET_SCRIPT,
            1,
            key,
            now_ms,
            capacity,
            refill_per_ms,
            ttl,
        )
        try:
            return int(allowed) == 1
        except Exception:
            return bool(allowed)
    except Exception as exc:
        log.warning("Google rate limiter Redis error: %s", exc)
        return True
