type RateLimitConfig = {
  windowMs: number;
  max: number;
};

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

export class RateLimitError extends Error {
  retryAfterSeconds: number;

  constructor(retryAfterSeconds: number) {
    super('rate_limited');
    this.retryAfterSeconds = retryAfterSeconds;
  }
}

function getConfig(): RateLimitConfig {
  const windowSeconds = Number(process.env.LEADS_RATE_LIMIT_WINDOW_SEC ?? '3600');
  const max = Number(process.env.LEADS_RATE_LIMIT_MAX ?? '20');

  return {
    windowMs: Math.max(1, windowSeconds) * 1000,
    max: Math.max(1, max),
  };
}

function getStore(): Map<string, Bucket> {
  const key = '__dropex_leads_rate_limit__';
  const globalAny = globalThis as unknown as Record<string, unknown>;
  if (!globalAny[key]) {
    globalAny[key] = new Map<string, Bucket>();
  }
  return globalAny[key] as Map<string, Bucket>;
}

export function rateLimitOrThrow(identity: string) {
  const { windowMs, max } = getConfig();
  const store = getStore();
  const now = Date.now();

  const existing = store.get(identity);
  if (!existing || existing.resetAt <= now) {
    store.set(identity, { resetAt: now + windowMs, count: 1 });
    return;
  }

  if (existing.count >= max) {
    const retryAfterSeconds = Math.max(1, Math.ceil((existing.resetAt - now) / 1000));
    throw new RateLimitError(retryAfterSeconds);
  }

  existing.count += 1;
  store.set(identity, existing);
}
