import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
  Logger,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';
import { LogLevel } from '../domain/loglevel.type';
import { LoggingInterceptorConfig } from '../domain/loginterceptorconfig.interface';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  private readonly logger = new Logger(LoggingInterceptor.name);
  private readonly pathConfig: Record<string, LogLevel>;

  constructor(config?: LoggingInterceptorConfig) {
    this.pathConfig = config?.pathConfig ?? { '/health': 'skip' };
  }

  public intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const logLevel = this.pathConfig[req.path] ?? 'full';

    if (logLevel === 'skip') {
      return next.handle();
    }

    if (!req.id) {
      req.id = this.generateRequestId();
    }
    const userId = req.user?.userId ?? '';

    const hasBody = req.body && Object.keys(req.body).length > 0;
    const includeBody = logLevel === 'full' && hasBody;

    let logMessage = `[Request ${req.id}] ${req.method} ${req.url} by user ${userId}`;

    if (includeBody) {
      logMessage += ` with body ${JSON.stringify(req.body, null, 2)}`;
    }

    this.logger.log(logMessage);

    const startOfRequest = Date.now();
    return next
      .handle()
      .pipe(
        tap(() =>
          this.logger.log(
            `[Request ${req.id}] Completed after... ${Date.now() - startOfRequest}ms`,
          ),
        ),
      );
  }

  private generateRequestId(): string {
    const randomString = Math.random().toString(36).substring(2, 8);
    return `${Date.now()}-${randomString}`;
  }
}
