import { AudioPlaybackContext } from '@/lib/useAudioPlayback';
import { use, useCallback, useContext, useEffect, useMemo, useRef } from 'react';

const Waveform = ({ bands }: { bands: number[][] }) => {
  const pathDef = useMemo(() => {
    const nodesPerBlock = Math.round(bands[0].length / 1000);
    const heightRounding = 1;
    let str = '';
    let maxY = 0;
    let lastX = 0;
    for (let i = 0 ; i < bands[0].length; i ++) {
      const x = (i / bands[0].length) * 1000;
      const y = ((bands.reduce((a, b) => a + b[i], 0) / bands.length) / 4) + 1;
      maxY = Math.ceil(Math.max(maxY, y) / heightRounding) * heightRounding;
      if (i % nodesPerBlock === 0) {
        str += `${i === 0 ? 'M' : 'L'} ${lastX} ${maxY} `;
        str += `L ${x} ${maxY} `;
        lastX = x;
        maxY = 0;
      }
    }
    for (let i = bands[0].length - 1 ; i >= 0; i --) {
      const x = (i / bands[0].length) * 1000;
      const y = ((bands.reduce((a, b) => a + b[i], 0) / bands.length) / 4) + 1;
      maxY = Math.ceil(Math.max(maxY, y) / heightRounding) * heightRounding;
      if (i % nodesPerBlock === 0) {
        str += `L ${lastX} ${-maxY} `;
        str += `L ${x} ${-maxY} `;
        lastX = x;
        maxY = 0;
      }
    }
    return str;
  }, [bands]);

  const playheadRectRef = useRef<SVGRectElement | null>(null);
  const playheadLineRef = useRef<SVGRectElement | null>(null);
  const audioPlayback = useContext(AudioPlaybackContext);
  const receiveWrapperRef = useCallback((el: HTMLDivElement | null) => {
    if (!el) return;
    let lastSeekTime = audioPlayback.getCurrentTime();

    const seekX = (clientX: number) => {
      const boundingRect = el.getBoundingClientRect();
      const x = clientX;
      const relativeX = x - boundingRect.left;
      const time = ((relativeX) / boundingRect.width) * audioPlayback.duration;
      const lineX = ((time / audioPlayback.duration) * 1000).toFixed(0);
      playheadLineRef.current?.setAttribute('x', lineX);
      playheadRectRef.current?.setAttribute('width', lineX);
      lastSeekTime = time;
      audioPlayback.seek(time);
    }

    el.addEventListener('mousedown', (e) => {
      let wasPlaying = audioPlayback.playing;
      if (audioPlayback.playing) {
        audioPlayback.stop(false);
      }
      seekX(e.clientX);
      const onMouseMove = (e: MouseEvent) => {
        seekX(e.clientX);
      }
      const onMouseUp = (e: MouseEvent) => {
        if (wasPlaying) {
          audioPlayback.play(lastSeekTime);
        }
        window.removeEventListener('mousemove', onMouseMove);
        window.removeEventListener('mouseup', onMouseUp);
      }
      window.addEventListener('mousemove', onMouseMove);
      window.addEventListener('mouseup', onMouseUp);
    });
  }, [audioPlayback]);

  useEffect(() => {
    const interval = setInterval(() => {
      if (!audioPlayback.duration) return;
      if (!playheadRectRef.current) return;
      const x = ((audioPlayback.getCurrentTime() / audioPlayback.duration) * 1000).toFixed(0);
      playheadRectRef.current.setAttribute('width', x);
      if (!playheadLineRef.current) return;
      playheadLineRef.current.setAttribute('x', x);
    }, 20);
    return () => clearInterval(interval);
  }, [audioPlayback]);

  return (
    <div ref={receiveWrapperRef} style={{ width: '100%', height: '100%' }}>
      <svg style={{ width: '100%', height: '100%' }} viewBox="0 -100 1000 200" preserveAspectRatio='none'>
        <clipPath id="waveform-clip">
          <path d={pathDef} />
        </clipPath>
        <g clipPath="url(#waveform-clip)">
          <rect x="0" y="-100" width="1000" height="200" fill="#3e3e3e" />
          <rect ref={playheadRectRef} x="0" y="-100" width="0" height="200" fill="#17706c" />
        </g>
        <rect ref={playheadLineRef} y="-100" x="0" width="1" height="200" fill="#ffc350" />
      </svg>
    </div>
  )
}

export default Waveform;
