import React, { useEffect, useMemo, useState } from 'react';

type Props = {
  file?: File | null;
  audioUrl?: string;
  title?: string;
  subtitle?: string;
  emptyMessage?: string;
  isLoading?: boolean;
};

const BAR_COUNT = 56;

function createWaveformBars(channelData: Float32Array) {
  const blockSize = Math.max(1, Math.floor(channelData.length / BAR_COUNT));
  const bars: number[] = [];

  for (let index = 0; index < BAR_COUNT; index += 1) {
    const start = index * blockSize;
    const end = Math.min(channelData.length, start + blockSize);
    let sum = 0;

    for (let sampleIndex = start; sampleIndex < end; sampleIndex += 1) {
      sum += Math.abs(channelData[sampleIndex]);
    }

    const average = end > start ? sum / (end - start) : 0;
    bars.push(Math.min(100, Math.max(8, Math.round(average * 280))));
  }

  return bars;
}

const AudioWaveformPreview = ({
  file,
  audioUrl,
  title = 'Waveform preview',
  subtitle,
  emptyMessage = 'Add an audio file to preview its waveform.',
  isLoading = false,
}: Props) => {
  const [bars, setBars] = useState<number[]>([]);
  const [errorMessage, setErrorMessage] = useState('');
  const previewUrl = useMemo(() => (file ? URL.createObjectURL(file) : audioUrl || ''), [audioUrl, file]);

  useEffect(() => {
    return () => {
      if (file && previewUrl) {
        URL.revokeObjectURL(previewUrl);
      }
    };
  }, [file, previewUrl]);

  useEffect(() => {
    let isActive = true;
    let audioContext: AudioContext | null = null;

    const buildWaveform = async () => {
      if (!file && !audioUrl) {
        setBars([]);
        setErrorMessage('');
        return;
      }

      try {
        setErrorMessage('');

        const audioBuffer = file ? await file.arrayBuffer() : await fetch(audioUrl as string).then((response) => response.arrayBuffer());

        if (typeof window === 'undefined' || !window.AudioContext) {
          throw new Error('AudioContext is unavailable in this browser.');
        }

        audioContext = new window.AudioContext();
        const decoded = await audioContext.decodeAudioData(audioBuffer.slice(0));

        if (!isActive) {
          return;
        }

        setBars(createWaveformBars(decoded.getChannelData(0)));
      } catch (error) {
        console.error('Failed to render waveform preview:', error);

        if (isActive) {
          setBars([]);
          setErrorMessage('Waveform preview is unavailable for this file, but the audio upload is still attached.');
        }
      } finally {
        if (audioContext && audioContext.state !== 'closed') {
          audioContext.close().catch(() => null);
        }
      }
    };

    buildWaveform();

    return () => {
      isActive = false;

      if (audioContext && audioContext.state !== 'closed') {
        audioContext.close().catch(() => null);
      }
    };
  }, [audioUrl, file]);

  return (
    <div className="rounded-2xl border border-white/10 bg-slate-950/70 p-4">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <div className="text-xs uppercase tracking-[0.18em] text-slate-500">{title}</div>
          <div className="mt-2 text-sm font-medium text-white">{subtitle || file?.name || 'Awaiting audio upload'}</div>
        </div>
        <div className="rounded-full border border-white/10 px-3 py-1 text-[11px] uppercase tracking-[0.16em] text-slate-400">
          {isLoading ? 'Rendering…' : previewUrl ? 'Ready to review' : 'No audio'}
        </div>
      </div>

      <div className="mt-4 flex h-24 items-end gap-1 overflow-hidden rounded-2xl border border-white/10 bg-slate-900/70 px-3 py-2">
        {bars.length ? (
          bars.map((barHeight, index) => (
            <div
              key={`${index}-${barHeight}`}
              className="min-w-[4px] flex-1 rounded-full bg-gradient-to-t from-violet-500 via-violet-300 to-emerald-300 opacity-90"
              style={{ height: `${barHeight}%` }}
            />
          ))
        ) : (
          <div className="flex h-full w-full items-center justify-center text-center text-sm text-slate-500">{emptyMessage}</div>
        )}
      </div>

      {previewUrl ? <audio controls className="mt-4 w-full" src={previewUrl} preload="metadata" /> : null}
      {errorMessage ? <p className="mt-3 text-xs leading-5 text-amber-200">{errorMessage}</p> : null}
    </div>
  );
};

export default AudioWaveformPreview;
