import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

type Entry = { count: number; resetAt: number };

const memoryStore = new Map<string, Entry>();

function pruneMemory() {
  const now = Date.now();
  for (const [k, v] of memoryStore) {
    if (v.resetAt < now) memoryStore.delete(k);
  }
}

/** Fallback mono-instance si Upstash n’est pas configuré. */
function rateLimitMemory(
  key: string,
  max: number,
  windowMs: number
): { ok: true } | { ok: false; retryAfterSec: number } {
  pruneMemory();
  const now = Date.now();
  let e = memoryStore.get(key);
  if (!e || e.resetAt < now) {
    e = { count: 0, resetAt: now + windowMs };
    memoryStore.set(key, e);
  }
  e.count += 1;
  if (e.count > max) {
    return {
      ok: false,
      retryAfterSec: Math.max(1, Math.ceil((e.resetAt - now) / 1000)),
    };
  }
  return { ok: true };
}

const WINDOW_MS = 15 * 60 * 1000;
const MAX_FAILED_PER_WINDOW = 8;

let upstashLimiter: Ratelimit | null | undefined;

function getUpstashLoginLimiter(): Ratelimit | null {
  if (upstashLimiter !== undefined) return upstashLimiter;
  if (
    !process.env.UPSTASH_REDIS_REST_URL ||
    !process.env.UPSTASH_REDIS_REST_TOKEN
  ) {
    upstashLimiter = null;
    return null;
  }
  upstashLimiter = new Ratelimit({
    redis: Redis.fromEnv(),
    limiter: Ratelimit.slidingWindow(MAX_FAILED_PER_WINDOW, "15 m"),
    prefix: "gauffre:login",
  });
  return upstashLimiter;
}

/**
 * Limite les échecs de connexion par identifiant (IP).
 * Avec `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN`, le compteur est partagé entre toutes les instances.
 */
export async function rateLimitFailedLogin(
  ip: string
): Promise<{ ok: true } | { ok: false; retryAfterSec: number }> {
  const limiter = getUpstashLoginLimiter();
  if (limiter) {
    const { success, reset } = await limiter.limit(ip);
    if (!success) {
      const retryAfterSec = Math.max(
        1,
        Math.ceil((reset - Date.now()) / 1000)
      );
      return { ok: false, retryAfterSec };
    }
    return { ok: true };
  }
  return rateLimitMemory(`login:${ip}`, MAX_FAILED_PER_WINDOW, WINDOW_MS);
}

export function getClientIp(request: Request): string {
  const xff = request.headers.get("x-forwarded-for");
  if (xff) {
    const first = xff.split(",")[0]?.trim();
    if (first) return first;
  }
  const realIp = request.headers.get("x-real-ip");
  if (realIp) return realIp.trim();
  return "unknown";
}
