const DEFAULT_SAMPLES = 48;

const scaleToRange = (values: number[]): number[] => {
  if (!values.length) {
    return [];
  }

  const max = Math.max(...values);
  if (max <= 0) {
    return values.map(() => 0);
  }

  return values.map((value) => Math.max(0, Math.min(100, Math.round((value / max) * 100))));
};

export const normalizeWaveformPeaks = (
  input: number[] | undefined,
  maxSamples = DEFAULT_SAMPLES,
): number[] => {
  if (!input?.length) {
    return [];
  }

  const cleaned = input
    .map((value) => (Number.isFinite(value) ? Math.abs(value) : 0))
    .filter((value) => value > 0);

  if (!cleaned.length) {
    return [];
  }

  if (cleaned.length <= maxSamples) {
    return scaleToRange(cleaned);
  }

  const windowSize = cleaned.length / maxSamples;
  const compressed: number[] = [];

  for (let i = 0; i < maxSamples; i += 1) {
    const start = Math.floor(i * windowSize);
    const end = Math.min(cleaned.length, Math.floor((i + 1) * windowSize));
    const slice = cleaned.slice(start, Math.max(start + 1, end));
    const peak = Math.max(...slice);
    compressed.push(peak);
  }

  return scaleToRange(compressed);
};

export const generateWaveformPeaksFromBuffer = (
  buffer: Buffer,
  samples = DEFAULT_SAMPLES,
): number[] => {
  if (!buffer.length) {
    return [];
  }

  const chunkSize = Math.max(1, Math.ceil(buffer.length / samples));
  const peaks: number[] = [];

  for (let offset = 0; offset < buffer.length; offset += chunkSize) {
    const chunk = buffer.subarray(offset, Math.min(buffer.length, offset + chunkSize));
    let total = 0;
    let localPeak = 0;

    for (const byte of chunk) {
      const centered = Math.abs(byte - 128);
      total += centered;
      localPeak = Math.max(localPeak, centered);
    }

    const average = chunk.length ? total / chunk.length : 0;
    peaks.push(Math.max(average, localPeak * 0.65));
  }

  return normalizeWaveformPeaks(peaks, samples);
};

export const generateWaveformPeaksFromSeed = (
  seed: string,
  samples = DEFAULT_SAMPLES,
): number[] => {
  const source = seed.trim() || 'audio';
  let hash = 2166136261;

  for (let i = 0; i < source.length; i += 1) {
    hash ^= source.charCodeAt(i);
    hash = Math.imul(hash, 16777619);
  }

  const peaks: number[] = [];
  let state = hash >>> 0;
  for (let i = 0; i < samples; i += 1) {
    state = (Math.imul(state, 1664525) + 1013904223) >>> 0;
    const base = 18 + (state % 65);
    const accent = i % 6 === 0 ? 18 : i % 3 === 0 ? 10 : 0;
    peaks.push(base + accent);
  }

  return normalizeWaveformPeaks(peaks, samples);
};
