/**
 * A sanitizer interface that provides methods to sanitize log entries.
 */
interface ISanitizer {
  /**
   * Sanitizes the provided log entry. Should be called only if canSanitize returns true.
   * @param {any} entry - The log entry to sanitize.
   * @returns {any} The sanitized log entry.
   */
  sanitize(entry: any): any;

  /**
   * Checks if the sanitizer can handle the given log entry.
   * @param {any} entry - The log entry to check.
   * @returns {boolean} True if the sanitizer can handle the log entry, false otherwise.
   */
  canSanitize(entry: any): boolean;
}

/**
 * A log sanitizer class that sanitizes logs to avoid leaking sensitive/private data.
 * only the first applicable sanitizer (as determined by canSanitize) will be applied
 * Currently, it is hardcoded to sanitize only logs from Redis rpush command.
 */
export class LogSanitizer {
  private _sanitizers: ISanitizer[] = [
    new RedisSanitizer(),
  ];

  /**
   * Sanitizes the provided log entry using a predefined set of sanitizers.
   * @param {any} log - The log entry to sanitize.
   * @returns {any} The sanitized log entry.
   */
  public sanitize(log: any): any {
    for (const sanitizer of this._sanitizers) {
      if (sanitizer.canSanitize(log)) {
        return sanitizer.sanitize(log);
      }
    }
    return log;
  }
}

/**
 * A sanitizer implementation for Redis logs.
 */
class RedisSanitizer implements ISanitizer {
  /**
   * Sanitizes the Redis log entry by replacing sensitive data in the payload with "[sanitized]".
   * @param {any} entry - The Redis log entry to sanitize.
   * @returns {any} The sanitized Redis log entry.
   */
  public sanitize(entry: any): any {
    // REDIS log structure looks like this: the first arg is the name of the queue,
    // and the rest are the data that was pushed to the queue. Therefore, we are omitting the first arg.
    for (let i = 1; i < entry.args.length; i++) {
      let arg = entry.args[i];
      let parsedArg: any = null;
      parsedArg = JSON.parse(arg);
      if (parsedArg?.payload) {
        parsedArg.payload = "[sanitized]";
      }
      arg = JSON.stringify(parsedArg);
      entry.args[i] = arg;
    }
    return entry;
  }

  /**
   * Checks if the given log entry corresponds to a Redis rpush command.
   * @param {any} entry - The log entry to check.
   * @returns {boolean} True if the log entry is a Redis rpush command, false otherwise.
   */
  public canSanitize(entry: any): boolean {
    // We are only interested in rpush commands
    return (
      typeof entry === "object" &&
      entry.command?.toLowerCase() === "rpush".toLowerCase() &&
      entry.args &&
      Array.isArray(entry.args)
    );
  }
}