import styled from '@emotion/styled';
import { useCallback, useEffect, useRef } from 'react';
import { twMerge } from 'tailwind-merge';

import useClickDrag from '@/hooks/useClickDrag';

import setCursor from './canvasRenderer/setCursor';

const Wrapper = styled.div`
  position: relative;
`;

const FaderWrapper = styled.div<{ visible: boolean }>`
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 200px;
  opacity: ${(props) => (props.visible ? 1 : 0)};
  pointer-events: ${(props) => (props.visible ? 'auto' : 'none')};
  padding-bottom: 100%; // this should match the width of the fader
  background-color: #1a1a1a;
  border-radius: 8px;
`;

const FaderInteractionArea = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: stretch;
  align-items: center;
  padding: 20px 0 8px 0;
  cursor: ns-resize;
`;

const FaderBackground = styled.div`
  width: 4px;
  border-radius: 2px;
  background-color: #484848;
  position: relative;
  height: 100%;
`;

const FaderForeground = styled.div`
  width: 4px;
  border-radius: 2px;
  background-color: #fff;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
`;

const FaderDot = styled.div`
  width: 12px;
  height: 12px;
  border-radius: 12px;
  border: 1px solid #1a1a1a;
  background-color: #fff;
  position: absolute;
  top: -4px;
  left: -4px;
`;

export default function PopupFader({
  visible,
  value,
  onChange,
  onCommit,
  onClose,
  children,
  className,
}: {
  visible: boolean;
  value: number;
  onChange: (newValue: number) => void;
  onCommit?: (newValue: number) => void;
  onClose: () => void;
  children: React.ReactNode[] | React.ReactNode;
  className?: string;
}) {
  const faderForegroundRef = useRef<HTMLDivElement>(null);
  const faderBackgroundRef = useRef<HTMLDivElement>(null);
  const clickDragRef = useClickDrag(
    useCallback(
      ({ clientY }) => {
        const faderBackgroundRect =
          faderBackgroundRef.current?.getBoundingClientRect();
        if (!faderBackgroundRect) return;
        const initialValue = Math.max(
          0,
          Math.min(
            1,
            1 - (clientY - faderBackgroundRect.top) / faderBackgroundRect.height
          )
        );
        const faderBackgroundHeight =
          faderBackgroundRef.current?.clientHeight || 150;
        const unsetCursor = setCursor('ns-resize');
        onChange(initialValue);
        return {
          onMouseMove: ({ deltaYFromStart }) => {
            const newValue = Math.min(
              1,
              Math.max(
                0,
                initialValue - deltaYFromStart / faderBackgroundHeight
              )
            );
            onChange(newValue);
            if (!faderForegroundRef.current) return;
            faderForegroundRef.current!.style.height = newValue * 100 + '%';
          },
          onMouseUp: ({ deltaYFromStart }) => {
            unsetCursor();
            onCommit?.(
              Math.min(
                1,
                Math.max(
                  0,
                  initialValue - deltaYFromStart / faderBackgroundHeight
                )
              )
            );
          },
        };
      },
      [value, onCommit, onChange]
    )
  );

  useEffect(() => {
    if (visible) {
      const handleKeydown = (e: KeyboardEvent) => {
        if (e.key === 'Escape') {
          onClose();
        }
      };
      const handleClick = (e: MouseEvent) => {
        if (!(e.target as HTMLElement)?.closest('.popup-fader')) {
          onClose();
        }
      };
      window.addEventListener('keydown', handleKeydown);
      window.addEventListener('click', handleClick);
      return () => {
        window.removeEventListener('keydown', handleKeydown);
        window.removeEventListener('click', handleClick);
      };
    }
  }, [visible, onClose]);

  return (
    <Wrapper className={twMerge('popup-fader', className || '')}>
      <FaderWrapper visible={visible}>
        <FaderInteractionArea ref={clickDragRef}>
          <FaderBackground ref={faderBackgroundRef}>
            <FaderForeground
              style={{ height: value * 100 + '%' }}
              ref={faderForegroundRef}
            >
              <FaderDot />
            </FaderForeground>
          </FaderBackground>
        </FaderInteractionArea>
      </FaderWrapper>
      {children}
    </Wrapper>
  );
}
