import { getRedis } from "./redis";

/**
 * 滑动窗口限流（基于 Redis INCR + EXPIRE）。
 *
 * 用法：
 *   const ok = await rateLimit(`vote:${userId}`, 60, 30); // 每 60s 最多 30 次
 *   if (!ok.allowed) return new Response('Too Many', { status: 429 });
 *
 * 降级：未配置 Redis 时使用内存 Map（仅适合开发环境）。
 */

interface RateLimitResult {
  allowed: boolean;
  remaining: number;
  resetAt: number;
}

// 开发兜底
const memoryStore = new Map<string, { count: number; expiresAt: number }>();

export async function rateLimit(
  key: string,
  windowSec: number,
  limit: number,
): Promise<RateLimitResult> {
  const now = Date.now();
  const redis = getRedis();

  if (redis) {
    const k = `rl:${key}`;
    try {
      const pipe = redis.pipeline();
      pipe.incr(k);
      pipe.expire(k, windowSec, "NX"); // 仅在首次设置 TTL
      pipe.pttl(k);
      const [[, count], , [, pttl]] = (await pipe.exec()) as [
        [Error | null, number],
        [Error | null, "OK" | 0],
        [Error | null, number],
      ];
      const remaining = Math.max(0, limit - count);
      const resetAt = pttl > 0 ? now + pttl : now + windowSec * 1000;
      return { allowed: count <= limit, remaining, resetAt };
    } catch (err) {
      console.error("[rate-limit] redis error, falling back to memory:", err);
    }
  }

  // 内存兜底
  const cur = memoryStore.get(key);
  if (!cur || cur.expiresAt < now) {
    memoryStore.set(key, { count: 1, expiresAt: now + windowSec * 1000 });
    return {
      allowed: 1 <= limit,
      remaining: limit - 1,
      resetAt: now + windowSec * 1000,
    };
  }
  cur.count += 1;
  return {
    allowed: cur.count <= limit,
    remaining: Math.max(0, limit - cur.count),
    resetAt: cur.expiresAt,
  };
}
