/**
 * Rate-limit primitive for Server Actions and API routes (KAR-517).
 *
 * Uses @upstash/ratelimit + Upstash Redis REST. When the Upstash env vars
 * are unset, the helper fails open and returns `allowed: true` — that is
 * the right behaviour for local dev and prevents accidental lock-out if
 * a misconfiguration sneaks in.
 *
 * Production-correct usage: every call site passes a distinct `key` so the
 * bucket is scoped to that user / action / resource combination, e.g.
 * `rate-limit:create-evaluation:${userId}`.
 *
 * Typical wiring (Server Action):
 *
 *   const result = await tryRateLimit(`evaluate:${userId}`, {
 *     limit: 30,
 *     window: "60 s",
 *   })
 *   if (!result.allowed) {
 *     return { ok: false, error: "rate-limited", retry_after: result.reset }
 *   }
 */
import { Ratelimit } from "@upstash/ratelimit"
import { Redis } from "@upstash/redis"

export interface RateLimitOptions {
  /** Max requests within `window`. */
  limit: number
  /**
   * Window expressed as ms-style duration string accepted by
   * `Ratelimit.slidingWindow` (e.g. "10 s", "60 s", "1 m", "1 h").
   */
  window: `${number} ${"ms" | "s" | "m" | "h" | "d"}`
}

export interface RateLimitResult {
  allowed: boolean
  identifier: string
  remaining: number
  reset: number
  reason: "ok" | "blocked" | "no-backend"
}

let _ratelimit: Ratelimit | null = null

function getRatelimit(opts: RateLimitOptions): Ratelimit | null {
  const url = process.env.UPSTASH_REDIS_REST_URL
  const token = process.env.UPSTASH_REDIS_REST_TOKEN
  if (!url || !token) return null

  if (!_ratelimit) {
    const redis = new Redis({ url, token })
    _ratelimit = new Ratelimit({
      redis,
      limiter: Ratelimit.slidingWindow(opts.limit, opts.window),
      analytics: true,
      prefix: "kadi:ratelimit",
    })
  }
  return _ratelimit
}

export async function tryRateLimit(
  identifier: string,
  opts: RateLimitOptions,
): Promise<RateLimitResult> {
  const rl = getRatelimit(opts)

  if (!rl) {
    return {
      allowed: true,
      identifier,
      remaining: opts.limit,
      reset: Date.now() + 60_000,
      reason: "no-backend",
    }
  }

  const verdict = await rl.limit(identifier)
  return {
    allowed: verdict.success,
    identifier,
    remaining: verdict.remaining,
    reset: verdict.reset,
    reason: verdict.success ? "ok" : "blocked",
  }
}
