import { buildMarkerSchedule } from './markerSchedule';
import { createSampleCache, type SampleCache } from './sampleCache';
import { audioBufferToWavBlob, EXPORT_SAMPLE_RATE } from './wav';
import type { Marker, SwitchType } from '../types';

export type RenderKeyboardTrackOptions = {
  /** Total track length in video-timeline seconds. */
  duration: number;
  switchType: SwitchType;
  markers: Marker[];
  /** Reuse decoded buffers from the live preview engine when available. */
  cache?: SampleCache;
};

/**
 * Render a keyboard-only WAV track at 1× using OfflineAudioContext.
 * Marker times are absolute video seconds — preview speed is not applied.
 */
export async function renderKeyboardTrack(options: RenderKeyboardTrackOptions): Promise<Blob> {
  const { duration, switchType, markers } = options;

  let decodeContext: AudioContext | null = null;
  const cache = options.cache ?? (() => {
    decodeContext = new AudioContext();
    return createSampleCache(decodeContext);
  })();

  try {
    const manifest = await cache.ensureManifest();
    const entry = manifest[switchType];
    if (!entry) throw new Error(`Unknown switch: ${switchType}`);

    if (cache.getLoadedSwitch() !== switchType) {
      await cache.preloadSwitch(switchType);
    }

    const frameCount = Math.ceil(duration * EXPORT_SAMPLE_RATE);
    const offline = new OfflineAudioContext(2, frameCount, EXPORT_SAMPLE_RATE);
    const schedule = buildMarkerSchedule(markers, entry);

    for (const event of schedule) {
      if (event.time >= duration) continue;
      if (event.phase === 'release' && event.sampleKey === 'GENERIC_long') continue;

      const buffer = await cache.loadBuffer(switchType, event.phase, event.sampleKey);
      const source = offline.createBufferSource();
      source.buffer = buffer;
      source.connect(offline.destination);
      source.start(event.time);
    }

    const rendered = await offline.startRendering();
    return audioBufferToWavBlob(rendered, EXPORT_SAMPLE_RATE);
  } finally {
    if (decodeContext) await decodeContext.close();
  }
}
