'use client';

/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import { useOutsideClick } from '@chakra-ui/react';
import clsx from 'clsx';
import { observer } from 'mobx-react-lite';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
// eslint-disable-next-line @suno-custom/no-react-icons -- grandfathered in, need to migrate
import { IconContext } from 'react-icons';

import { useStores } from '@/app/(root)/AppProviders';
import { PauseIcon, PlayIcon } from '@/icons';
import { Clip } from '@/state/clipStore';

interface PlaybarProps {
  width?: number;
  truncate?: boolean;
  hidden?: boolean;
  inline?: boolean;
  showScrubber?: boolean;
  clip?: Clip;
  disableControls?: boolean;
  togglePlay?: () => void;
}

interface PlayControlsProps {
  handlePlayerTogglePlay: () => void;
  clip: Clip | null;
  inline?: boolean;
  disableControls?: boolean;
}

const PlayControls = observer(
  ({
    handlePlayerTogglePlay,
    clip,
    disableControls = false,
  }: PlayControlsProps) => {
    const { playbar } = useStores();

    return (
      <>
        {playbar.isPlaying ? (
          <button
            aria-label='Playbar: Pause button'
            className='flex h-[24px] w-[24px] items-center justify-center p-0 outline-none'
            disabled={disableControls}
            onClick={() => {
              handlePlayerTogglePlay();
            }}
          >
            <PauseIcon
              className={clsx(`h-[16px] w-[16px]`, {
                'fill-foreground-inactive': disableControls,
                'fill-foreground-primary': !disableControls,
              })}
            />
          </button>
        ) : (
          <button
            aria-label='Playbar: Play button'
            className={clsx(
              'flex h-[24px] w-[24px] items-center justify-center p-0 outline-none',
              {
                'fill-foreground-inactive': disableControls,
                'fill-foreground-primary': !disableControls,
              }
            )}
            disabled={disableControls}
            onClick={() => {
              if (clip && !playbar.isClipPreloaded) {
                playbar.togglePlay();
              } else {
                playbar.noClipPlayCallback?.();
              }
            }}
          >
            <PlayIcon
              className={clsx('h-[16px] w-[16px]', {
                'fill-foreground-inactive': disableControls,
                'fill-foreground-primary': !disableControls,
              })}
            />
          </button>
        )}
      </>
    );
  }
);

const Playbar: React.FC<PlaybarProps> = observer(
  ({
    width,
    hidden = false,
    inline = false,
    showScrubber = true,
    clip: _clip,
    disableControls = false,
    togglePlay,
  }: PlaybarProps) => {
    const { playbar, clips } = useStores();

    const audioRef = React.useRef<HTMLAudioElement>(null);
    // The silent audio element is used to keep the MediaSession alive
    const silentAudioRef = React.useRef<HTMLAudioElement>(null);

    const widthPixels = width || 966;
    const playbarRef = useRef<any>(null);
    const elapsedBarRef = useRef<any>(null);
    const elapsedThumbRef = useRef<any>(null);
    const volumeSliderRef = useRef<any>(null);
    const clip =
      _clip || (playbar.clip?.id ? clips.clipById[playbar.clip?.id] : null);

    useEffect(() => {
      if (typeof window !== 'undefined') {
        const handleBeforeUnload = () => {
          playbar.updatePlaybarState();
          window.removeEventListener('beforeunload', handleBeforeUnload);
        };
        window.addEventListener('beforeunload', handleBeforeUnload);
        return () => {
          window.removeEventListener('beforeunload', handleBeforeUnload);
        };
      }
    }, [playbar]);

    useLayoutEffect(() => {
      if (audioRef.current && !inline) {
        playbar.setAudioElement(audioRef.current);
      }
    }, [audioRef.current, playbar]);

    useLayoutEffect(() => {
      if (silentAudioRef.current && !inline) {
        playbar.setSilentAudioElement(silentAudioRef.current);
      }
    }, [silentAudioRef.current]);

    useEffect(() => {
      if (!playbarRef.current) return;

      const resizeObserver = new ResizeObserver(() => {
        const leftPos = playbarRef.current
          ? Math.floor(
              playbarRef.current?.getBoundingClientRect().width *
                Math.min(playbar.currentTime / playbar.duration, 1.0)
            )
          : null;
        if (elapsedBarRef.current && elapsedThumbRef.current) {
          elapsedBarRef.current.style.width = `${leftPos}px`;
          elapsedThumbRef.current.style.left = `${leftPos}px`;
        }
      });
      resizeObserver.observe(playbarRef.current);
    }, [playbarRef.current]);

    useHotkeys(
      'space',
      (e) => {
        handlePlayerTogglePlay();
        e.preventDefault();
      },
      []
    );

    /**
     * Handles the click event for the play/pause button
     */
    const handlePlayerTogglePlay = () => {
      const clip =
        _clip || (playbar.clip?.id ? clips.clipById[playbar.clip?.id] : null);
      if (playbar.loadingStream) {
        // TODO: add new tracking on needs
      }
      if (clip && !playbar.isClipPreloaded) {
        playbar.togglePlay();
      } else {
        playbar.noClipPlayCallback?.();
      }
    };

    const [isDragging, setIsDragging] = useState(false);
    const [currentDragX, setCurrentDragX] = useState<number | null>(null);
    const [isVolumeSliderOpen, setIsVolumeSliderOpen] = useState(false);

    useEffect(() => {
      if (clip?.metadata.duration && playbar.loadingDuration) {
        playbar.setDuration(clip?.metadata.duration);
        playbar.setLoadingDuration(false);
        playbar.setClip(clip);
      }
    }, [clip?.metadata.duration]);

    useOutsideClick({
      ref: volumeSliderRef,
      handler: () => {
        if (isVolumeSliderOpen) {
          setIsVolumeSliderOpen(false);
        }
      },
    });

    useEffect(() => {
      // Prevent highlighting on the page when dragging playbar
      if (isDragging) {
        document.body.classList.add('no-drag');
      } else {
        document.body.classList.remove('no-drag');
      }
      playbar.setIsDragging(isDragging);
    }, [isDragging]);

    useEffect(() => {
      if (!isDragging && currentDragX !== null) {
        setCurrentDragX(null);
      }
    }, [isDragging, currentDragX]);
    return (
      <>
        <div
          className={`z-[25] w-full rounded-none bg-transparent lg:rounded-lg xl:rounded-lg ${hidden ? 'hidden' : ''} touch-none`}
        >
          <div
            className={`bottom-0 z-5 h-[24px] w-full min-w-[300px] rounded-t-xl`}
            onPointerMove={(e: any) => {
              if (disableControls) return;

              if (isDragging) {
                // TODO debounce?
                setCurrentDragX(
                  Math.min(
                    e.clientX,
                    playbarRef.current?.getBoundingClientRect().left +
                      playbarRef.current?.getBoundingClientRect().width
                  )
                );
              }
            }}
            onPointerUp={() => {
              if (disableControls) return;

              if (isDragging && currentDragX && clip?.metadata.duration) {
                setIsDragging(false);
                setCurrentDragX(null);
                const seekPosLeft =
                  currentDragX -
                  playbarRef.current?.getBoundingClientRect().left;
                playbar.userSetCurrentProgress(
                  (seekPosLeft /
                    playbarRef.current?.getBoundingClientRect().width) *
                    100
                );
                requestAnimationFrame(() => {
                  elapsedBarRef.current.style.width = `${seekPosLeft}px`;
                  elapsedThumbRef.current.style.left = `${seekPosLeft}px`;
                });
              }
            }}
            onPointerLeave={() => {
              if (disableControls) return;

              if (currentDragX && clip?.metadata.duration) {
                playbar.userSetCurrentProgress(
                  ((currentDragX -
                    playbarRef.current?.getBoundingClientRect().left) /
                    playbarRef.current?.getBoundingClientRect().width) *
                    100
                );
              }
              setIsDragging(false);
              setCurrentDragX(null);
            }}
          >
            <div
              className={`flex h-[24px] flex-1 flex-row items-center gap-[14px] overflow-x-hidden overflow-y-hidden rounded-xl font-sans text-sm font-medium text-foreground-primary`}
            >
              <div className='flex flex-1 flex-row content-between items-center p-0'>
                <IconContext.Provider
                  value={{ className: 'stroke-white fill-white text-xl' }}
                >
                  <div className={`w-4 flex-1 flex-row justify-center gap-4`}>
                    <PlayControls
                      handlePlayerTogglePlay={
                        togglePlay ? togglePlay : handlePlayerTogglePlay
                      }
                      clip={clip}
                      inline={inline}
                      disableControls={disableControls}
                    />
                  </div>
                </IconContext.Provider>
              </div>
              {widthPixels && showScrubber && (
                <div
                  ref={playbarRef}
                  className={clsx(
                    'flex h-[4px] w-full flex-row rounded-[50px] bg-[#414653]',
                    {
                      'cursor-pointer': !disableControls,
                    }
                  )}
                  onPointerDown={() => {
                    if (disableControls || !playbar.duration) return;

                    if (clip) {
                      setIsDragging(true);
                    }
                  }}
                  onPointerUp={() => {
                    if (disableControls || !playbar.duration) return;

                    setIsDragging(false);
                    setCurrentDragX(null);
                  }}
                  onClick={(e) => {
                    if (disableControls || !playbar.duration) return;

                    if (clip) {
                      setIsDragging(true);
                      const leftPos =
                        e.clientX -
                        e.currentTarget.getBoundingClientRect().left;
                      playbar.userSetCurrentProgress(
                        (leftPos /
                          e.currentTarget.getBoundingClientRect().width) *
                          100
                      );
                      const maxLeftPos = Math.min(
                        leftPos,
                        e.currentTarget.getBoundingClientRect().width
                      );
                      requestAnimationFrame(() => {
                        setIsDragging(false);
                        setCurrentDragX(null);
                        elapsedBarRef.current.style.width = `${maxLeftPos}px`;
                        elapsedThumbRef.current.style.left = `${maxLeftPos}px`;
                      });
                    }
                  }}
                >
                  <div
                    className={`h-[4px] rounded-[50px] bg-accent-brand ${
                      !isDragging
                        ? 'transition transition-[width] duration-525 ease-linear'
                        : ''
                    }`}
                    ref={elapsedBarRef}
                    onPointerDown={() => {
                      if (clip) {
                        setIsDragging(true);
                      }
                    }}
                    onPointerUp={() => {
                      setIsDragging(false);
                      setCurrentDragX(null);
                    }}
                    style={{
                      width: currentDragX
                        ? `${
                            currentDragX -
                            playbarRef.current?.getBoundingClientRect().left
                          }px`
                        : `${Math.floor(
                            playbarRef.current?.getBoundingClientRect().width *
                              Math.min(
                                playbar.currentTime / playbar.duration,
                                1.0
                              )
                          )}px`,
                    }}
                  ></div>
                </div>
              )}
            </div>
          </div>
        </div>
      </>
    );
  }
);

export default Playbar;
