/**
 * AudioValidationPipe
 *
 * NestJS PipeTransform that validates uploaded audio files.
 * Checks MIME type against an allow-list and file size against a configurable maximum.
 *
 * Usage:
 * ```typescript
 * @Post('transcribe')
 * @UseInterceptors(FileInterceptor('file'))
 * async transcribe(
 *   @UploadedFile(new AudioValidationPipe()) file: Express.Multer.File,
 * ) { ... }
 * ```
 *
 * Issue #398
 */

import { BadRequestException } from "@nestjs/common";
import type { PipeTransform } from "@nestjs/common";

/**
 * Default accepted MIME types for audio uploads.
 */
const DEFAULT_ALLOWED_MIME_TYPES: readonly string[] = [
  "audio/wav",
  "audio/mp3",
  "audio/mpeg",
  "audio/webm",
  "audio/ogg",
  "audio/flac",
  "audio/x-m4a",
] as const;

/**
 * Default maximum upload size in bytes (25 MB).
 */
const DEFAULT_MAX_FILE_SIZE = 25_000_000;

/**
 * Options for customizing AudioValidationPipe behavior.
 */
export interface AudioValidationPipeOptions {
  /** Maximum file size in bytes. Defaults to 25 MB. */
  maxFileSize?: number;

  /** List of accepted MIME types. Defaults to common audio formats. */
  allowedMimeTypes?: string[];
}

/**
 * Format bytes into a human-readable string (e.g., "25.0 MB").
 */
function formatBytes(bytes: number): string {
  if (bytes < 1024) {
    return `${String(bytes)} B`;
  }
  if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(1)} KB`;
  }
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

export class AudioValidationPipe implements PipeTransform<Express.Multer.File | undefined> {
  private readonly maxFileSize: number;
  private readonly allowedMimeTypes: readonly string[];

  constructor(options?: AudioValidationPipeOptions) {
    this.maxFileSize = options?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
    this.allowedMimeTypes = options?.allowedMimeTypes ?? DEFAULT_ALLOWED_MIME_TYPES;
  }

  /**
   * Validate the uploaded file's MIME type and size.
   *
   * @param file - The uploaded file from Multer
   * @returns The validated file, unchanged
   * @throws {BadRequestException} If the file is missing, has an unsupported MIME type, or exceeds the size limit
   */
  transform(file: Express.Multer.File | undefined): Express.Multer.File {
    if (!file) {
      throw new BadRequestException("No audio file provided");
    }

    // Validate MIME type
    if (!this.allowedMimeTypes.includes(file.mimetype)) {
      throw new BadRequestException(
        `Unsupported audio format: ${file.mimetype}. ` +
          `Supported formats: ${this.allowedMimeTypes.join(", ")}`
      );
    }

    // Validate file size
    if (file.size > this.maxFileSize) {
      throw new BadRequestException(
        `File size ${formatBytes(file.size)} exceeds maximum allowed size of ${formatBytes(this.maxFileSize)}`
      );
    }

    return file;
  }
}
