export const WAVEFORM_BARS = 40;

// One character per bar (36 levels) so the waveform can ride in the voice message file name.
const LEVELS = "0123456789abcdefghijklmnopqrstuvwxyz";

export const encodePeaks = (peaks: number[]) =>
  peaks.map((peak) => LEVELS[Math.min(35, Math.max(0, Math.round(peak * 35)))]).join("");

const decodePeaks = (code: string) => [...code.toLowerCase()].map((char) => Math.max(0, LEVELS.indexOf(char)) / 35);

/** voice-message-<time>-<seconds>s-w<levels>.<ext> */
export const peaksFromFileName = (name?: string | null): number[] | null => {
  const match = name?.match(/-w([0-9a-z]+)\.[a-z0-9]+$/i);
  return match ? decodePeaks(match[1]) : null;
};

/** Loudness per bar (0..1, normalised to the loudest bar) and the exact duration. */
export const analyseAudio = async (data: ArrayBuffer, bars = WAVEFORM_BARS): Promise<{ peaks: number[], duration: number } | null> => {
  try {
    const Context = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext;
    const context = new Context();
    const buffer = await context.decodeAudioData(data);
    void context.close();

    const channel = buffer.getChannelData(0);
    const size = Math.max(1, Math.floor(channel.length / bars));
    const peaks: number[] = [];
    for (let bar = 0; bar < bars; bar++) {
      let sum = 0;
      const start = bar * size;
      const end = Math.min(channel.length, start + size);
      for (let i = start; i < end; i++) sum += channel[i] * channel[i];
      peaks.push(Math.sqrt(sum / Math.max(1, end - start)));
    }
    const loudest = Math.max(...peaks) || 1;

    return { peaks: peaks.map((peak) => peak / loudest), duration: buffer.duration };
  } catch {
    return null;
  }
};

/** Stable, natural-looking bars for voice messages recorded before waveforms were saved. */
export const placeholderPeaks = (seed: string, bars = WAVEFORM_BARS): number[] => {
  let state = 0;
  for (let i = 0; i < seed.length; i++) state = (state * 31 + seed.charCodeAt(i)) >>> 0;
  const next = () => {
    state = (state * 1664525 + 1013904223) >>> 0;
    return state / 4294967296;
  };
  let previous = 0.5;
  return Array.from({ length: bars }, () => {
    previous = Math.min(1, Math.max(0.15, previous * 0.5 + next() * 0.6));
    return previous;
  });
};
