/**
 * In-memory rate limiter (single-instance only).
 *
 * For multi-instance deployments, replace with @upstash/ratelimit +
 * @upstash/redis. The API below mirrors the upstash `Ratelimiter.limit`
 * shape so the swap is straightforward.
 *
 * Entries are pruned lazily on each call to keep the Map bounded.
 */

interface RateLimitEntry {
  count: number
  resetTime: number
}

const rateLimitMap = new Map<string, RateLimitEntry>()

/** Prune entries whose window has elapsed. Called on every limit check. */
function pruneExpired(now: number) {
  if (rateLimitMap.size === 0) return
  for (const [key, entry] of rateLimitMap.entries()) {
    if (entry.resetTime <= now) {
      rateLimitMap.delete(key)
    }
  }
}

export interface RateLimitResult {
  success: boolean
  remaining: number
  resetTime: number
  /** Milliseconds until the window resets (0 when already reset). */
  retryAfterMs: number
}

export interface RateLimitParams {
  /** Unique identifier for the bucket (e.g. `login:${ip}` or `ai:${userId}`). */
  key: string
  /** Maximum number of requests allowed within the window. */
  limit: number
  /** Window size in milliseconds. */
  windowMs: number
}

/**
 * Check whether a request should be allowed under the given rate limit.
 * Increments the counter regardless of success (so repeated failures
 * accumulate).
 */
export function rateLimit(params: RateLimitParams): RateLimitResult {
  const now = Date.now()
  pruneExpired(now)

  const existing = rateLimitMap.get(params.key)

  if (!existing || existing.resetTime <= now) {
    // Start a fresh window
    const resetTime = now + params.windowMs
    rateLimitMap.set(params.key, { count: 1, resetTime })
    return {
      success: true,
      remaining: params.limit - 1,
      resetTime,
      retryAfterMs: 0,
    }
  }

  // Within an existing window
  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,
  }
}

/**
 * Reset the counter for a key. Useful when a successful action should
 * clear the failure count (e.g. successful login clears LOGIN limit).
 */
export function resetRateLimit(key: string): void {
  rateLimitMap.delete(key)
}

/**
 * Predefined rate limit rules for common scenarios.
 * Times are in milliseconds.
 */
export const RATE_LIMIT_RULES = {
  LOGIN: { limit: 5, windowMs: 15 * 60 * 1000 }, // 5 attempts per 15 minutes
  API: { limit: 100, windowMs: 60 * 1000 }, // 100 requests per minute
  UPLOAD: { limit: 10, windowMs: 60 * 1000 }, // 10 uploads per minute
  AI_CHAT: { limit: 20, windowMs: 60 * 1000 }, // 20 chats per minute
  PASSWORD_CHANGE: { limit: 5, windowMs: 60 * 1000 }, // 5 attempts per minute
} as const

/**
 * Build a rate-limit key from a prefix and identifier.
 */
export function rateLimitKey(prefix: string, identifier: string): string {
  return `${prefix}:${identifier}`
}

/**
 * Convert a RateLimitResult into standard HTTP response headers.
 */
export function rateLimitHeaders(result: RateLimitResult): Record<string, string> {
  return {
    "X-RateLimit-Limit": String(result.remaining + (result.success ? 1 : 0)),
    "X-RateLimit-Remaining": String(result.remaining),
    "X-RateLimit-Reset": String(Math.ceil(result.resetTime / 1000)),
    ...(result.success ? {} : { "Retry-After": String(Math.ceil(result.retryAfterMs / 1000)) }),
  }
}
