// Pre-compute audio peaks for waveform visualisation. We use OfflineAudioContext
// to decode the file once, then bucket samples into N windows and store the
// peak (max abs) of each window. The result is a small Float32Array that's
// cheap to render in <canvas> or <svg> on every state change.

export interface WaveformPeaks {
  /** Positive peaks (0..1), one per bucket. */
  positive: Float32Array;
  /** Negative peaks (0..1), one per bucket. Always non-negative magnitude. */
  negative: Float32Array;
  duration: number;
}

const DEFAULT_BUCKETS = 256;

export async function computeWaveformPeaks(
  file: Blob | ArrayBuffer,
  buckets = DEFAULT_BUCKETS,
): Promise<WaveformPeaks> {
  const AC = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext;
  const ctx = new OfflineAudioContext(1, 44100, 44100);
  const arrayBuffer = file instanceof Blob ? await file.arrayBuffer() : file;
  const buffer = await ctx.decodeAudioData(arrayBuffer.slice(0));
  const data = buffer.getChannelData(0);
  const positive = new Float32Array(buckets);
  const negative = new Float32Array(buckets);
  const samplesPerBucket = Math.max(1, Math.floor(data.length / buckets));
  for (let i = 0; i < buckets; i++) {
    const start = i * samplesPerBucket;
    const end = Math.min(start + samplesPerBucket, data.length);
    let max = 0;
    let min = 0;
    for (let j = start; j < end; j++) {
      const v = data[j];
      if (v > max) max = v;
      if (v < min) min = v;
    }
    positive[i] = max;
    negative[i] = -min; // store as positive magnitude
  }
  return { positive, negative, duration: buffer.duration };
}

/** Cached peaks per asset id. Stored in a module-level Map so we don't
 * re-decode on every render. */
const peaksCache = new Map<string, WaveformPeaks | Promise<WaveformPeaks>>();

export function getOrComputePeaks(
  assetId: string,
  source: Blob | ArrayBuffer,
): WaveformPeaks | Promise<WaveformPeaks> {
  const cached = peaksCache.get(assetId);
  if (cached) return cached;
  const promise = computeWaveformPeaks(source).then((p) => {
    peaksCache.set(assetId, p);
    return p;
  });
  peaksCache.set(assetId, promise);
  return promise;
}

export function getCachedPeaks(assetId: string): WaveformPeaks | null {
  const v = peaksCache.get(assetId);
  if (v && !(v instanceof Promise)) return v;
  return null;
}

export function clearPeaksCache(): void {
  peaksCache.clear();
}