import { keyframes } from '@emotion/react';
import styled from '@emotion/styled';
import { observer } from 'mobx-react-lite';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';

import { FocusedObjectContext } from '@/app/(root)/create/v2/useFocusedObject';
import useClip from '@/hooks/useClip';
import { useContextSelector } from '@/hooks/useContextSelector';
import { isLiked } from '@/state/clipStore';

import { ResponsiveChild, useContainer } from '../containerQueries/components';
import { RightClickMenuTrigger } from '../contextMenu/ContextMenu';
import StudioSongRowWrapper from '../studio/StudioSongRowWrapper';
import { useClipWarmup } from '../studio/useClipWarmup';
import {
  ClipContextProvider,
  NullableClipContextProvider,
} from './ClipContext';
import {
  ClipBPM,
  ClipDetailsWrapper,
  ClipDotAndCheckbox,
  ClipExactTags,
  ClipHasStem,
  ClipImage,
  ClipInProject,
  ClipKey,
  ClipMetadataRow,
  ClipModelVersion,
  ClipRowGroup,
  ClipRowHeightShort,
  ClipRowWrapper,
  ClipTitle,
  StudioClipInteractions,
} from './ClipElements';
import { MoreMenuContents } from './ClipMenus';
import { ClipPlaybackProvider, useStudioClipPlayback } from './useClipPlayback';
import virtualizeClipRow from './virtualizeClipRow';

const HoverExpanderWrapper = styled.div`
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 3;
`;

const fadeIn = keyframes`
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
`;

const Positioner = styled.div`
  position: fixed;
  z-index: 10000000;
  border-radius: 16px;
  margin-left: -4px;
  background-color: var(--color-background-secondary);
  animation: ${fadeIn} 0.1s ease-in-out both;
`;

const HoverExpander = ({
  ContentComponent,
  width,
  height,
}: {
  ContentComponent: React.ComponentType<any>;
  width: number;
  height: number;
}) => {
  const [show, setShow] = useState(false);
  const wrapperRef = useRef<HTMLDivElement>(null);
  const positionerRef = useRef<HTMLDivElement>(null);
  const receivePositionerRef = useCallback((node: HTMLDivElement) => {
    if (node) {
      positionerRef.current = node;
      const wrapper = wrapperRef.current;
      if (wrapper) {
        const wrapperRect = wrapper.getBoundingClientRect();
        node.style.left = `${wrapperRect.left}px`;
        node.style.top = `${wrapperRect.top}px`;
      }
    } else {
      positionerRef.current = null;
    }
  }, []);

  const mousemoveListenerRef = useRef<(e: MouseEvent) => void | null>(null);

  const handleMouseEnter = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      e.stopPropagation();
      e.preventDefault();
      setShow(true);
      const enterTime = Date.now();
      const handleMouseMove = (e: MouseEvent) => {
        if (
          positionerRef.current &&
          !(e.target as HTMLElement)?.closest('.hover-expander-positioner')
        ) {
          setShow(false);
          window.removeEventListener('mousemove', handleMouseMove);
        } else if (!positionerRef.current && enterTime + 1000 < Date.now()) {
          setShow(false);
          window.removeEventListener('mousemove', handleMouseMove);
        }
      };
      mousemoveListenerRef.current = handleMouseMove;
      window.addEventListener('mousemove', handleMouseMove);
    },
    []
  );

  const handleMouseLeave = useCallback(
    (e: React.MouseEvent<HTMLDivElement>) => {
      e.stopPropagation();
      e.preventDefault();
      setShow(false);
      if (mousemoveListenerRef.current) {
        window.removeEventListener('mousemove', mousemoveListenerRef.current);
        mousemoveListenerRef.current = null;
      }
    },
    []
  );

  useEffect(() => {
    return () => {
      if (mousemoveListenerRef.current) {
        window.removeEventListener('mousemove', mousemoveListenerRef.current);
        mousemoveListenerRef.current = null;
      }
    };
  }, []);

  const fadeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  return (
    <HoverExpanderWrapper ref={wrapperRef} onMouseEnter={handleMouseEnter}>
      {show &&
        ReactDOM.createPortal(
          <Positioner
            onWheel={(e) => {
              if (wrapperRef?.current) {
                wrapperRef.current
                  .closest('.clip-browser-list-scroller')
                  ?.scrollBy({
                    left: (e as any).deltaX,
                    top: (e as any).deltaY,
                  });
                if (positionerRef.current) {
                  positionerRef.current.style.opacity = '0.5';
                  if (fadeTimeoutRef.current) {
                    clearTimeout(fadeTimeoutRef.current);
                  }
                  fadeTimeoutRef.current = setTimeout(() => {
                    if (positionerRef.current) {
                      positionerRef.current.style.opacity = '1';
                    }
                    fadeTimeoutRef.current = null;
                  }, 500);
                }
              }
            }}
            className='hover-expander-positioner'
            ref={receivePositionerRef}
            style={{ width, height }}
            onMouseLeave={handleMouseLeave}
          >
            <ContentComponent />
          </Positioner>,
          document.body
        )}
    </HoverExpanderWrapper>
  );
};

export const StudioClipRow = observer(
  ({ clipId, isHoverPopup }: { clipId: string; isHoverPopup?: boolean }) => {
    const { clip } = useClip(clipId);
    const { warmupClip } = useClipWarmup();
    useEffect(() => {
      if (clip?.id && clip?.status === 'complete') {
        const timeout = setTimeout(() => warmupClip(clip.id), 1000);
        return () => {
          clearTimeout(timeout);
        };
      }
    }, [clip?.id, clip?.status, warmupClip]);

    const [isMoreMenuOpen, setIsMoreMenuOpen] = useState(false);
    const clipPlayback = useStudioClipPlayback(clip);
    const { containerName, containerRef } = useContainer('clip-row');
    const isFocusedClip = useContextSelector(
      FocusedObjectContext,
      (ctx) => (ctx.focusedObject as any)?.clipId === clipId
    );
    const keepHovering = isMoreMenuOpen || isFocusedClip;
    const HoverExpanderComponent = useCallback(
      () => <StudioClipRow clipId={clipId} isHoverPopup={true} />,
      [clipId]
    );

    return (
      <ClipPlaybackProvider value={clipPlayback}>
        <NullableClipContextProvider clip={clip}>
          <RightClickMenuTrigger ContentsComponent={MoreMenuContents}>
            <div className='relative h-[64px]' ref={containerRef}>
              {!isHoverPopup && (
                <ResponsiveChild
                  showBelowWidth={200}
                  containerName={containerName}
                >
                  <HoverExpander
                    width={300}
                    height={64}
                    ContentComponent={HoverExpanderComponent}
                  />
                </ResponsiveChild>
              )}

              <StudioSongRowWrapper
                clipId={clipId}
                bypassPlaybackAutoScroll={true}
                childrenHeight={ClipRowHeightShort}
              >
                <ClipRowWrapper
                  className='clip-row'
                  keepHovering={keepHovering}
                  height={ClipRowHeightShort}
                >
                  <ResponsiveChild
                    hideBelowWidth={200}
                    containerName={containerName}
                  >
                    <ClipRowGroup className='-ml-1'>
                      {!isHoverPopup && (
                        <>
                          <ClipDotAndCheckbox />
                          <div className='-mr-3 w-1 flex-0' />
                        </>
                      )}
                      <ClipImage small />
                      <ClipDetailsWrapper className='-mt-[5px]'>
                        <ClipTitle
                          link={false}
                          tags={false}
                          className='-mb-[5px]'
                        />
                        <ClipExactTags className='-mt-[1px]' />
                        <ClipMetadataRow className='-mt-[2px]'>
                          <ClipInProject />
                          <ClipModelVersion />
                          <ClipHasStem />
                          <ClipBPM />
                          <ClipKey />
                        </ClipMetadataRow>
                      </ClipDetailsWrapper>
                    </ClipRowGroup>
                  </ResponsiveChild>
                  {!isHoverPopup && (
                    <ResponsiveChild
                      showBelowWidth={200}
                      containerName={containerName}
                    >
                      <ClipRowGroup className='-ml-2'>
                        <ClipImage small />
                      </ClipRowGroup>
                    </ResponsiveChild>
                  )}
                  <ResponsiveChild
                    hideBelowWidth={200}
                    containerName={containerName}
                  >
                    <ClipRowGroup
                      className={`shrink-0 ${clip && isLiked(clip) ? 'hover-fade-in' : 'hover-only'}`}
                    >
                      {clip && (
                        <ClipContextProvider clip={clip}>
                          <StudioClipInteractions
                            containerName={containerName}
                            setIsMoreMenuOpen={setIsMoreMenuOpen}
                          />
                        </ClipContextProvider>
                      )}
                    </ClipRowGroup>
                  </ResponsiveChild>
                </ClipRowWrapper>
              </StudioSongRowWrapper>
            </div>
          </RightClickMenuTrigger>
        </NullableClipContextProvider>
      </ClipPlaybackProvider>
    );
  }
);

export const VirtualizedStudioClipRow = virtualizeClipRow(StudioClipRow, () => (
  <ClipRowWrapper height={ClipRowHeightShort} />
));
