const FFMPEG_MODULE_URL = '/public/vendor/ffmpeg/ffmpeg.js';
const FFMPEG_CORE_BASE_URL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/esm';

async function blobUrl(fetchImpl, createObjectURL, url, mimeType) {
  const response = await fetchImpl(url);
  if (!response.ok) throw new Error(`Failed to load FFmpeg resource: ${url}`);
  return createObjectURL(new Blob([await response.arrayBuffer()], { type: mimeType }));
}

export function createFfmpegAdapter({
  fetchImpl = fetch,
  importModule = specifier => import(specifier),
  createObjectURL = value => URL.createObjectURL(value),
  revokeObjectURL = value => URL.revokeObjectURL(value),
  onProgress = () => {},
  onLog = () => {},
} = {}) {
  let instance = null;

  return {
    async load() {
      if (instance) return instance;
      const { FFmpeg } = await importModule(FFMPEG_MODULE_URL);
      const candidate = new FFmpeg();
      candidate.on('progress', ({ progress }) => onProgress(progress));
      candidate.on('log', data => onLog(data));

      const objectUrls = await Promise.all([
        blobUrl(fetchImpl, createObjectURL, `${FFMPEG_CORE_BASE_URL}/ffmpeg-core.js`, 'text/javascript'),
        blobUrl(fetchImpl, createObjectURL, `${FFMPEG_CORE_BASE_URL}/ffmpeg-core.wasm`, 'application/wasm'),
      ]);
      try {
        await candidate.load({
          coreURL: objectUrls[0],
          wasmURL: objectUrls[1],
        });
      } finally {
        for (const url of objectUrls) revokeObjectURL(url);
      }
      instance = candidate;
      return instance;
    },

    terminate() {
      instance?.terminate?.();
      instance = null;
    },
  };
}
