import * as React from 'react';
import {WaveSurferOptions} from 'wavesurfer.js';
import clsx from 'clsx';
import {getFormValues} from '@theoryofnekomata/formxtra';

type SpectrogramCanvasDerivedElement = HTMLDivElement;

export interface SpectrogramCanvasProps
  extends React.HTMLProps<SpectrogramCanvasDerivedElement>,
    Omit<WaveSurferOptions, 'waveColor' | 'plugins' | 'height' | 'media' | 'container' | 'fillParent' | 'url' | 'autoplay' | 'renderFunction'> {
  waveColor?: string;
  audioRef?: React.Ref<HTMLAudioElement>;
}

export const SpectrogramCanvas = React.forwardRef<SpectrogramCanvasDerivedElement, SpectrogramCanvasProps>(({
  className,
  children,
  controls,
  waveColor,
  progressColor,
  cursorColor,
  cursorWidth,
  barWidth,
  barGap,
  barRadius,
  barHeight,
  barAlign,
  minPxPerSec,
  peaks,
  duration,
  autoPlay,
  interact,
  hideScrollbar,
  audioRate,
  autoScroll,
  autoCenter,
  sampleRate,
  splitChannels,
  normalize,
  audioRef,
  ...etcProps
}, forwardedRef) => {
  const [isPlaying, setIsPlaying] = React.useState(false);
  const defaultRef = React.useRef<SpectrogramCanvasDerivedElement>(null);
  const containerRef = forwardedRef ?? defaultRef;
  const waveSurferRef = React.useRef<any>(null);
  const cursorRef = React.useRef<HTMLDivElement>(null);

  const handleAction: React.FormEventHandler<HTMLFormElement> = (e) => {
    e.preventDefault();
    const nativeEvent = e.nativeEvent as unknown as { submitter: HTMLElement };
    const formData = getFormValues(
      e.currentTarget,
      {
        submitter: nativeEvent.submitter,
      }
    );
    const actionName = formData['action'] as string;
    switch (actionName) {
    case 'togglePlayback':
      setIsPlaying((prev) => !prev);
      break;
    default:
      break;
    }
  };

  React.useEffect(() => {
    if (!(typeof audioRef === 'object' && audioRef)) {
      return;
    }
    const { current: media } = audioRef;
    if (!media) {
      return;
    }

    if (!(typeof containerRef === 'object' && containerRef)) {
      return;
    }
    const { current: container } = containerRef;
    if (!container) {
      return;
    }

    if (!(typeof cursorRef === 'object' && cursorRef)) {
      return;
    }
    const { current: cursor } = cursorRef;
    if (!cursor) {
      return;
    }

    const handleTimeUpdate = (e: Event) => {
      const thisMedia = e.currentTarget as HTMLAudioElement;
      cursor.style.width = `${(thisMedia?.currentTime ?? 0) / (thisMedia?.duration ?? 1) * 100}%`;
    };

    const load = async (media: HTMLAudioElement, container: HTMLElement) => {
      const { default: WaveSurfer } = await import('wavesurfer.js');
      const { default: Spectrogram, } = await import('wavesurfer.js/dist/plugins/spectrogram');
      const dummyContainer = window.document.createElement('div');
      window.document.body.appendChild(dummyContainer);

      const waveSurferInstance = WaveSurfer.create({
        container: dummyContainer,
        height: 100,
        fillParent: true,
        autoplay: autoPlay,
        waveColor,
        progressColor,
        cursorColor,
        barWidth,
        barGap,
        barRadius,
        barHeight,
        barAlign,
        minPxPerSec,
        peaks,
        duration,
        interact,
        hideScrollbar,
        audioRate,
        autoScroll,
        autoCenter,
        sampleRate,
        splitChannels,
        normalize,
        plugins: [],
        cursorWidth,
        media,
      });

      let colorMap: Array<[number, number, number, number]> = [];
      if (waveColor?.toLowerCase().startsWith('rgb(')) {
        const waveColorParse = waveColor.match(/rgb\((\d+)[, ]\s*(\d+)[, ]\s*(\d+)\)/);
        const waveColorR = parseInt(waveColorParse?.[1] ?? '0', 10);
        const waveColorG = parseInt(waveColorParse?.[2] ?? '0', 10);
        const waveColorB = parseInt(waveColorParse?.[3] ?? '0', 10);
        for (let i = 0; i < 256; i += 1) {
          colorMap.push([waveColorR / 256, waveColorG / 256, waveColorB / 256, i / 256]);
        }
      }
      waveSurferInstance.registerPlugin(
        Spectrogram.create({
          container,
          labels: true,
          labelsColor: 'rgb(0 0 0/0)',
          height: container.clientHeight,
          colorMap,
        }),
      )

      waveSurferInstance.on('ready', () => {
        if (!container) {
          return;
        }
        while (container.children.length > 1) {
          container.removeChild(container.children[0]);
        }
        dummyContainer.remove();
      });
      await waveSurferInstance.load(media.currentSrc);
      waveSurferInstance.setTime(media.currentTime);
      media.addEventListener('timeupdate', handleTimeUpdate);
      return waveSurferInstance;
    };
    const { current: waveSurferCurrent } = waveSurferRef;

    void load(media, container).then((i) => {
      waveSurferRef.current = i;
    });

    return () => {
      if (waveSurferCurrent) {
        (waveSurferCurrent as unknown as Record<string, Function>).destroy();
      }
      if (container) {
        container.innerHTML = '';
      }
      if (media) {
        media.removeEventListener('timeupdate', handleTimeUpdate);
      }
    };
  }, [
    audioRef,
    autoPlay,
    waveColor,
    progressColor,
    cursorColor,
    barWidth,
    barGap,
    barRadius,
    barHeight,
    barAlign,
    minPxPerSec,
    peaks,
    duration,
    interact,
    hideScrollbar,
    audioRate,
    autoScroll,
    autoCenter,
    sampleRate,
    splitChannels,
    normalize,
    cursorWidth,
    containerRef,
  ]);

  return (
    <div
      className={clsx(
        'flex flex-col',
        className,
      )}
    >
      <div
        className="flex-auto relative aspect-video sm:aspect-auto"
        style={{
          maskImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACEAYAAAAiJtFnAAAFDmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgeG1wOkNyZWF0ZURhdGU9IjIwMjMtMDYtMjdUMTk6MTE6MTQrMDgwMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjMtMDYtMjdUMTk6MTI6MjMrMDg6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMDYtMjdUMTk6MTI6MjMrMDg6MDAiCiAgIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAyMy0wNi0yN1QxOToxMToxNCswODAwIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICB0aWZmOkltYWdlV2lkdGg9IjIiCiAgIHRpZmY6SW1hZ2VMZW5ndGg9IjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjAuNCIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wNi0yN1QxOToxMjoyMyswODowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+HFYtUQAAAYFpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHLS0JBFIc/tRdZFNQiqIWEtaowA6lNkBEWSIgZ9Nro1auB2uVeI6Jt0DYoiNr0WtRfUNugdRAURRBta13UpuR2rgpK5BnOnG9+M+cwcwbskbSSMWo8kMnm9HDA75qbX3DVv1JHJ05gIKoY2lgoFKSqfT1gs+Jdv1Wr+rl/zRlPGArYGoRHFU3PCU8KB9dymsW7wu1KKhoXPhfu0+WCwveWHivym8XJIv9YrEfC42BvFXYlKzhWwUpKzwjLy3Fn0qtK6T7WS5oS2dkZid3iXRiECeDHxRQTjONjkBGZffTjZUBWVMn3FPKnWZFcRWaNdXSWSZIiR5+oq1I9IVEVPSEjzbrV/799NdQhb7F6kx9qX0zzowfqdyC/bZrfx6aZPwHHM1xly/krRzD8Kfp2WXMfQssmXFyXtdgeXG5Bx5MW1aMFySFuV1V4P4PmeWi7hcbFYs9K+5w+QmRDvuoG9g+gV863LP0CQBln1EZARokAAAAJcEhZcwAACxMAAAsTAQCanBgAAAASSURBVAiZY2BAASzdqHyG//8BDTECjpzQZHQAAAAASUVORK5CYII=)',
          WebkitMaskImage: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACEAYAAAAiJtFnAAAFDmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS41LjAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgeG1wOkNyZWF0ZURhdGU9IjIwMjMtMDYtMjdUMTk6MTE6MTQrMDgwMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjMtMDYtMjdUMTk6MTI6MjMrMDg6MDAiCiAgIHhtcDpNZXRhZGF0YURhdGU9IjIwMjMtMDYtMjdUMTk6MTI6MjMrMDg6MDAiCiAgIHBob3Rvc2hvcDpEYXRlQ3JlYXRlZD0iMjAyMy0wNi0yN1QxOToxMToxNCswODAwIgogICBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIgogICBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iMiIKICAgZXhpZjpDb2xvclNwYWNlPSIxIgogICB0aWZmOkltYWdlV2lkdGg9IjIiCiAgIHRpZmY6SW1hZ2VMZW5ndGg9IjIiCiAgIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiCiAgIHRpZmY6WFJlc29sdXRpb249IjcyLzEiCiAgIHRpZmY6WVJlc29sdXRpb249IjcyLzEiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJwcm9kdWNlZCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWZmaW5pdHkgUGhvdG8gMiAyLjAuNCIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMy0wNi0yN1QxOToxMjoyMyswODowMCIvPgogICAgPC9yZGY6U2VxPgogICA8L3htcE1NOkhpc3Rvcnk+CiAgPC9yZGY6RGVzY3JpcHRpb24+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+HFYtUQAAAYFpQ0NQc1JHQiBJRUM2MTk2Ni0yLjEAACiRdZHLS0JBFIc/tRdZFNQiqIWEtaowA6lNkBEWSIgZ9Nro1auB2uVeI6Jt0DYoiNr0WtRfUNugdRAURRBta13UpuR2rgpK5BnOnG9+M+cwcwbskbSSMWo8kMnm9HDA75qbX3DVv1JHJ05gIKoY2lgoFKSqfT1gs+Jdv1Wr+rl/zRlPGArYGoRHFU3PCU8KB9dymsW7wu1KKhoXPhfu0+WCwveWHivym8XJIv9YrEfC42BvFXYlKzhWwUpKzwjLy3Fn0qtK6T7WS5oS2dkZid3iXRiECeDHxRQTjONjkBGZffTjZUBWVMn3FPKnWZFcRWaNdXSWSZIiR5+oq1I9IVEVPSEjzbrV/799NdQhb7F6kx9qX0zzowfqdyC/bZrfx6aZPwHHM1xly/krRzD8Kfp2WXMfQssmXFyXtdgeXG5Bx5MW1aMFySFuV1V4P4PmeWi7hcbFYs9K+5w+QmRDvuoG9g+gV863LP0CQBln1EZARokAAAAJcEhZcwAACxMAAAsTAQCanBgAAAASSURBVAiZY2BAASzdqHyG//8BDTECjpzQZHQAAAAASUVORK5CYII=)',
        }}
      >
        <div
          ref={cursorRef}
          style={{
            position: 'absolute',
            top: 0,
            left: 0,
            height: '100%',
            mixBlendMode: 'plus-lighter',
            backgroundColor: 'rgb(var(--color-primary))',
            zIndex: 5,
          }}
        />
        <div className="absolute top-0 left-0 w-full h-full"
          ref={containerRef}
        />
      </div>
      {controls && (
        <form
          onSubmit={handleAction}
        >
          <button
            type="submit"
            name="action"
            value="togglePlayback"
          >
            {isPlaying ? '⏸' : '▶'}
          </button>
        </form>
      )}
    </div>
  );
});

SpectrogramCanvas.displayName = 'WavesurferCanvas';
