import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';

import { SecurityIpLogService } from './security-ip-log.service';

/**
 * SecurityIpLogInterceptor - Global request logging for law enforcement compliance
 *
 * Applied globally to log all HTTP requests with raw IP addresses.
 * Operates independently of consent-free analytics:
 * - Analytics: session IDs held in memory, no device storage
 * - This interceptor: logs raw IPs server-side for legal compliance
 *
 * Fire-and-forget: never blocks the request, silently fails if logging fails.
 */
@Injectable()
export class SecurityIpLogInterceptor implements NestInterceptor {
  constructor(private readonly securityIpLogService: SecurityIpLogService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const request = context.switchToHttp().getRequest();

    // Extract IP from various proxy headers
    const ipAddress = this.extractIp(request);

    // Extract session ID from header (sent by analytics client)
    const sessionId = request.headers['x-session-id'] || 'unknown';

    // Extract authenticated user ID if available
    const userId = request.user?.id ?? request.user?.userId ?? undefined;

    // Fire-and-forget: don't await, don't block the request
    this.securityIpLogService
      .log({
        ipAddress,
        sessionId,
        userId,
        requestPath: request.path || request.url,
        requestMethod: request.method,
        userAgent: request.headers['user-agent'],
      })
      .catch(() => {
        // Silent failure - security logging should never block user requests
      });

    return next.handle();
  }

  /**
   * Extract real client IP from proxy headers.
   *
   * Priority:
   * 1. X-Forwarded-For (first IP in chain)
   * 2. X-Real-IP
   * 3. request.ip (direct connection)
   */
  private extractIp(request: {
    headers: Record<string, string | string[] | undefined>;
    ip?: string;
  }): string {
    // X-Forwarded-For can be a comma-separated list; take the first (client) IP
    const xForwardedFor = request.headers['x-forwarded-for'];
    if (xForwardedFor) {
      const firstIp = Array.isArray(xForwardedFor)
        ? xForwardedFor[0]
        : xForwardedFor.split(',')[0];
      return firstIp.trim();
    }

    // X-Real-IP is set by nginx
    const xRealIp = request.headers['x-real-ip'];
    if (xRealIp) {
      return Array.isArray(xRealIp) ? xRealIp[0] : xRealIp;
    }

    // Fallback to direct connection IP
    return request.ip || '0.0.0.0';
  }
}
