// =============================================================================
// FFmpeg Adapter
// Converts between audio formats using FFmpeg WASM via Web Worker
// =============================================================================

import * as Comlink from 'comlink';
import type {
  IFileConverter,
  SupportedFormat,
  ConversionRequest,
  AudioFormat,
} from '../core/types';
import { getFormatCategory } from '../core/format-utils';
import type { AudioConverterWorker } from '../workers/audio.worker';

type WorkerType = Comlink.Remote<AudioConverterWorker>;
let workerInstance: WorkerType | null = null;

function getWorker(): WorkerType {
  if (!workerInstance) {
    const worker = new Worker(
      new URL('../workers/audio.worker.ts', import.meta.url),
      { type: 'module' }
    );
    workerInstance = Comlink.wrap<AudioConverterWorker>(worker);
  }
  return workerInstance;
}

const SUPPORTED_FORMATS: AudioFormat[] = [
  'mp3', 'wav', 'ogg', 'm4a', 'flac',
  'aac', 'aiff', 'alac', 'wma', 'opus', 'm4r', 'amr'
];

export class FfmpegAdapter implements IFileConverter {
  readonly name = 'ffmpeg-adapter';

  supports(inputFormat: SupportedFormat, outputFormat: SupportedFormat): boolean {
    // Both must be audio
    if (getFormatCategory(inputFormat) !== 'audio' || getFormatCategory(outputFormat) !== 'audio') {
      return false;
    }

    return (
      SUPPORTED_FORMATS.includes(inputFormat as AudioFormat) &&
      SUPPORTED_FORMATS.includes(outputFormat as AudioFormat)
    );
  }

  async convert(
    request: ConversionRequest,
    onProgress?: (progress: number) => void
  ): Promise<Blob> {
    const { file, targetFormat, options } = request;
    const bitrate = options?.bitrate ?? 192;

    onProgress?.(5);

    // Read file into ArrayBuffer
    const arrayBuffer = await file.arrayBuffer();
    onProgress?.(10);

    const inputFormat = this.detectInputFormat(file);

    const worker = getWorker();
    
    // Wrap progress callback for Comlink proxy
    const progressProxy = onProgress
      ? Comlink.proxy((p: number) => {
          // Scale from 10-95% to leave room for file reading and finalization
          onProgress(10 + Math.round(p * 0.85));
        })
      : undefined;

    const result = await worker.convert(
      arrayBuffer,
      inputFormat,
      targetFormat as AudioFormat,
      bitrate,
      progressProxy
    );

    onProgress?.(100);
    return result;
  }

  private detectInputFormat(file: File): AudioFormat {
    const mimeToFormat: Record<string, AudioFormat> = {
      'audio/mpeg': 'mp3',
      'audio/mp3': 'mp3',
      'audio/wav': 'wav',
      'audio/wave': 'wav',
      'audio/ogg': 'ogg',
      'audio/mp4': 'm4a',
      'audio/x-m4a': 'm4a',
      'audio/aac': 'm4a',
      'audio/flac': 'flac',
    };

    const format = mimeToFormat[file.type];
    if (format) return format;

    // Fallback to extension
    const ext = file.name.split('.').pop()?.toLowerCase();
    if (ext && SUPPORTED_FORMATS.includes(ext as AudioFormat)) {
      return ext as AudioFormat;
    }

    throw new Error(`Unable to detect audio format for file: ${file.name}`);
  }
}

// Export singleton instance
export const ffmpegAdapter = new FfmpegAdapter();
