import type { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';
import { loadFFmpeg } from '@/lib/wasm/wasmLoader';
import type { ConversionOptions, ProgressCallback, ConversionResult } from '@/types/conversion';

/**
 * Convert video/audio using FFmpeg
 */
export async function convertWithFFmpeg(
  file: File,
  outputFormat: string,
  options: ConversionOptions = {},
  onProgress?: ProgressCallback
): Promise<ConversionResult> {
  const startTime = Date.now();

  try {
    // Load FFmpeg instance
    const ffmpeg: FFmpeg = await loadFFmpeg();

    // Set up progress tracking
    if (onProgress) {
      ffmpeg.on('progress', ({ progress }) => {
        onProgress(Math.round(progress * 100));
      });
    }

    // Input filename
    const inputName = file.name;
    const outputName = `output.${outputFormat}`;

    // Write input file to FFmpeg virtual file system
    await ffmpeg.writeFile(inputName, await fetchFile(file));

    // Build FFmpeg command based on format and options
    const args = buildFFmpegArgs(inputName, outputName, outputFormat, options);

    console.log('[FFmpeg] Running command:', args.join(' '));

    // Execute FFmpeg command
    await ffmpeg.exec(args);

    // Read output file
    const data = await ffmpeg.readFile(outputName);
    const blob = new Blob([data as BlobPart], { type: getMimeType(outputFormat) });

    // Clean up virtual file system
    await ffmpeg.deleteFile(inputName);
    await ffmpeg.deleteFile(outputName);

    const duration = Date.now() - startTime;

    return {
      success: true,
      blob,
      duration,
    };
  } catch (error) {
    console.error('[FFmpeg] Conversion error:', error);

    return {
      success: false,
      error: error instanceof Error ? error.message : 'Unknown conversion error',
      duration: Date.now() - startTime,
    };
  }
}

/**
 * Build FFmpeg command arguments
 */
function buildFFmpegArgs(
  inputName: string,
  outputName: string,
  outputFormat: string,
  options: ConversionOptions
): string[] {
  const args = ['-i', inputName];

  // Video codec
  if (options.videoCodec) {
    args.push('-c:v', options.videoCodec);
  }

  // Video bitrate
  if (options.videoBitrate) {
    args.push('-b:v', options.videoBitrate);
  }

  // Video resolution
  if (options.videoResolution) {
    args.push('-s', options.videoResolution);
  }

  // Video FPS
  if (options.videoFps) {
    args.push('-r', options.videoFps.toString());
  }

  // Audio codec
  if (options.audioCodec) {
    args.push('-c:a', options.audioCodec);
  }

  // Audio bitrate
  if (options.audioBitrate) {
    args.push('-b:a', options.audioBitrate);
  }

  // Audio sample rate
  if (options.audioSampleRate) {
    args.push('-ar', options.audioSampleRate.toString());
  }

  // Audio channels
  if (options.audioChannels) {
    args.push('-ac', options.audioChannels.toString());
  }

  // Format-specific settings
  switch (outputFormat) {
    case 'webm':
      // Use VP8 by default (less memory-intensive than VP9)
      if (!options.videoCodec) args.push('-c:v', 'libvpx');
      if (!options.audioCodec) args.push('-c:a', 'libvorbis');
      // Optimize for faster encoding and lower memory usage
      args.push('-deadline', 'realtime');
      args.push('-cpu-used', '8');
      // Set quality/bitrate if not specified
      if (!options.videoBitrate) args.push('-b:v', '1M');
      if (!options.audioBitrate) args.push('-b:a', '128k');
      break;
    case 'mp4':
      if (!options.videoCodec) args.push('-c:v', 'libx264');
      if (!options.audioCodec) args.push('-c:a', 'aac');
      // Use faster preset for browser encoding
      args.push('-preset', 'ultrafast');
      args.push('-tune', 'zerolatency');
      if (!options.videoBitrate) args.push('-b:v', '1M');
      if (!options.audioBitrate) args.push('-b:a', '128k');
      break;
    case 'mp3':
      if (!options.audioCodec) args.push('-c:a', 'libmp3lame');
      args.push('-vn'); // No video
      break;
    case 'wav':
      if (!options.audioCodec) args.push('-c:a', 'pcm_s16le');
      args.push('-vn'); // No video
      break;
    case 'ogg':
      if (!options.audioCodec) args.push('-c:a', 'libvorbis');
      args.push('-vn'); // No video
      break;
    case 'gif':
      // For GIF, use filter to optimize
      args.push('-vf', 'fps=15,scale=480:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse');
      break;
  }

  // Output file
  args.push(outputName);

  return args;
}

/**
 * Get MIME type for output format
 */
function getMimeType(format: string): string {
  const mimeTypes: Record<string, string> = {
    mp4: 'video/mp4',
    webm: 'video/webm',
    avi: 'video/x-msvideo',
    mov: 'video/quicktime',
    mkv: 'video/x-matroska',
    mp3: 'audio/mpeg',
    wav: 'audio/wav',
    ogg: 'audio/ogg',
    aac: 'audio/aac',
    flac: 'audio/flac',
    gif: 'image/gif',
  };

  return mimeTypes[format] || 'application/octet-stream';
}

/**
 * Extract audio from video
 */
export async function extractAudio(
  file: File,
  outputFormat: string = 'mp3',
  onProgress?: ProgressCallback
): Promise<ConversionResult> {
  return convertWithFFmpeg(
    file,
    outputFormat,
    {
      audioCodec: outputFormat === 'mp3' ? 'libmp3lame' : undefined,
      audioBitrate: '192k',
    },
    onProgress
  );
}

/**
 * Convert video to GIF
 */
export async function videoToGif(
  file: File,
  fps: number = 15,
  width: number = 480,
  onProgress?: ProgressCallback
): Promise<ConversionResult> {
  return convertWithFFmpeg(
    file,
    'gif',
    {
      videoFps: fps,
      videoResolution: `${width}:-1`,
    },
    onProgress
  );
}
