/**
 * 内存滑动窗口速率限制器（audit-P1-6）
 *
 * 默认实现，无需额外依赖。
 *
 * 适用场景：
 * - 单实例部署（开发环境、小型站点）
 * - 进程内限流（不跨实例共享计数）
 *
 * 不适用场景：
 * - 多实例部署（K8s 水平扩容、Serverless 多实例）
 * - 需要持久化计数的场景
 *
 * 多实例部署时请通过 `RATE_LIMIT_DRIVER=redis` 切换至 `RedisRateLimiter`。
 */

import type { RateLimiter, RateLimitParams, RateLimitResult } from "./types"

interface RateLimitEntry {
  count: number
  resetTime: number
}

/**
 * 内存实现的速率限制器。
 *
 * 使用单个进程级 `Map` 存储计数，懒清理过期项保持 Map 有界。
 * 虽然内部操作是同步的，但 `limit` / `reset` 仍返回 Promise，以兼容 `RateLimiter` 接口。
 */
export class MemoryRateLimiter implements RateLimiter {
  private readonly store = new Map<string, RateLimitEntry>()

  /** 清理已过期的桶，避免 Map 无限增长。每次 `limit` 调用时执行。 */
  private pruneExpired(now: number): void {
    if (this.store.size === 0) return
    for (const [key, entry] of this.store.entries()) {
      if (entry.resetTime <= now) {
        this.store.delete(key)
      }
    }
  }

  async limit(params: RateLimitParams): Promise<RateLimitResult> {
    const now = Date.now()
    this.pruneExpired(now)

    const existing = this.store.get(params.key)

    if (!existing || existing.resetTime <= now) {
      // 新窗口
      const resetTime = now + params.windowMs
      this.store.set(params.key, { count: 1, resetTime })
      return {
        success: true,
        remaining: params.limit - 1,
        resetTime,
        retryAfterMs: 0,
      }
    }

    // 窗口内累加计数
    existing.count += 1
    const remaining = Math.max(0, params.limit - existing.count)
    const success = existing.count <= params.limit
    const retryAfterMs = success ? 0 : existing.resetTime - now

    return {
      success,
      remaining,
      resetTime: existing.resetTime,
      retryAfterMs,
    }
  }

  async reset(key: string): Promise<void> {
    this.store.delete(key)
  }
}
