import {
  Injectable,
  CanActivate,
  ExecutionContext,
  HttpException,
  HttpStatus,
} from '@nestjs/common';

const WINDOW_MS = 60 * 1000;
const LIMIT = 60;

/**
 * Strict per-(user:device:ip) limiter — port of Express `sensitiveRateLimit`
 * (60 requests / 60s). Used on money/credit/abuse-sensitive routes (billing
 * verify, ad rewards, AI detect-breed/dispute, report create/my-pet). Throws
 * `429 SENSITIVE_RATE_LIMIT` when the window is exceeded.
 *
 * Uses an in-memory fixed-window store with lazy pruning — matches the
 * single-instance behaviour of the Express limiter.
 */
@Injectable()
export class SensitiveRateLimitGuard implements CanActivate {
  private readonly hits = new Map<string, { count: number; resetAt: number }>();

  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest();

    const userId = String(req.user_id || req.user?.id || '').trim();
    const deviceId = String(req.headers['x-device-id'] || req.headers['device-id'] || '').trim();
    const ip = String(req.ip || req.socket?.remoteAddress || '').trim();
    const key = `${userId || 'anon'}:${deviceId || 'nodevice'}:${ip}`;

    const now = Date.now();
    const entry = this.hits.get(key);

    if (!entry || entry.resetAt <= now) {
      this.hits.set(key, { count: 1, resetAt: now + WINDOW_MS });
      this.prune(now);
      return true;
    }

    entry.count += 1;
    if (entry.count > LIMIT) {
      throw new HttpException(
        {
          status: false,
          statusCode: 429,
          message: 'Too many sensitive requests. Please retry shortly.',
          code: 'SENSITIVE_RATE_LIMIT',
        },
        HttpStatus.TOO_MANY_REQUESTS,
      );
    }

    return true;
  }

  /** Drop expired windows occasionally so the map doesn't grow unbounded. */
  private prune(now: number) {
    if (this.hits.size < 1000) return;
    for (const [k, v] of this.hits) {
      if (v.resetAt <= now) this.hits.delete(k);
    }
  }
}
