import styled from '@emotion/styled';
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';

import { Tooltip } from '../tooltip/Tooltip';
import SelectionContext from './SelectionContext';
import setCursor from './canvasRenderer/setCursor';
import { GlassModule, GlassModuleBody, GlassModuleHeader } from './components';
import AlignedLyricsContext from './lyrics/AlignedLyricsContext';
import { getPlaintextLyrics } from './lyrics/getPlaintextLyrics';

const TextDisplay = styled.div`
  height: 100%;
  overflow: auto;
  white-space: pre-wrap;
  font-size: 18px;
  line-height: 1.5;
  padding: 4px;
  user-select: none;
  span {
    white-space: pre-wrap;
  }
`;

const TextEditor = styled.textarea`
  height: 100%;
  width: 100%;
  font-size: 18px;
  line-height: 1.5;
  padding: 4px;
  border: none;
  resize: none;
  background-color: transparent;
  &:focus {
    outline: none;
  }
`;

const UnselectedLyrics = styled.span`
  opacity: 0.5;
`;

const SelectedLyrics = styled.span`
  opacity: 1;
  background-image: linear-gradient(
    #ee7b00aa 0%,
    #ee7b00aa 5%,
    #ee7b0044 5%,
    #ee7b0044 95%,
    #ee7b00aa 95%,
    #ee7b00aa 100%
  );
`;

const GrabHandle = styled.span`
  display: inline-block;
  height: 18px;
  width: 1px;
  position: relative;
  cursor: ew-resize;
  &:hover:before {
    background-color: #ee7b00;
  }
  &:active:before {
    background-color: #ee7b00;
  }
  &:before {
    content: '';
    position: absolute;
    top: -2px;
    left: -2px;
    border-radius: 2px;
    width: 4px;
    height: 27px;
    background-color: #ee7b00aa;
  }
  &:after {
    content: '';
    position: absolute;
    top: -2px;
    left: -10px;
    border-radius: 2px;
    width: 20px;
    height: 28px;
    z-index: 2;
  }
`;

export const OldLyrics = () => {
  const hasScrolledRef = useRef(false);
  const replacementRef = useRef<HTMLSpanElement>(null);
  const textDisplayRef = useRef<HTMLDivElement>(null);
  const {
    songLyrics,
    replacementConfirmation,
    setReplacementConfirmation,
    updateReplacementConfirmation,
  } = useContext(AlignedLyricsContext);
  const plaintextPreEditLyrics = useMemo(
    () => getPlaintextLyrics(songLyrics),
    [songLyrics]
  );

  const selectionContext = useContext(SelectionContext);

  useEffect(() => {
    if (!hasScrolledRef.current && replacementRef.current) {
      replacementRef.current.scrollIntoView({ block: 'center' });
      hasScrolledRef.current = true;
    }
  }, [plaintextPreEditLyrics]);

  useEffect(() => {
    if (!!replacementConfirmation) {
      updateReplacementConfirmation(
        songLyrics,
        selectionContext.selectionStartSeconds,
        selectionContext.selectionEndSeconds
      );
    }
    setTimeout(() => {
      replacementRef.current?.scrollIntoView({ block: 'center' });
    });
  }, [
    songLyrics,
    !!replacementConfirmation,
    updateReplacementConfirmation,
    selectionContext.selectionStartSeconds,
    selectionContext.selectionEndSeconds,
  ]);

  const handlePreGrabMouseDown = useCallback(() => {
    const unsetCursor = setCursor('ew-resize');
    const handleMouseMove = (e: MouseEvent) => {
      const target = e.target as HTMLElement;
      if (target.matches('[data-word-index]')) {
        const targetRect = target.getBoundingClientRect();
        const targetWidth = targetRect.width;
        const mouseX = e.clientX - targetRect.left;
        let wordIndex = parseInt(target.dataset.wordIndex!);
        const isInRightHalfOfElement = mouseX > targetWidth / 2;
        if (isInRightHalfOfElement) {
          wordIndex += 1;
        }
        setReplacementConfirmation((prev) => {
          if (!prev) return prev;
          const prevStartIndex = prev.originalLyricsSplit[1]?.[0]?.index || 0;
          if (prevStartIndex === wordIndex) return prev;

          const allLyrics = prev.originalLyricsSplit.flat();
          const endIndex = Math.max(
            prev.originalLyricsSplit[2]?.[0]?.index || Infinity
          );
          wordIndex = Math.max(0, Math.min(wordIndex, endIndex - 1));
          return {
            ...prev,
            originalLyricsSplit: [
              allLyrics.filter(({ index }) => index < wordIndex),
              allLyrics.filter(
                ({ index }) => index >= wordIndex && index < endIndex
              ),
              allLyrics.filter(({ index }) => index >= endIndex),
            ],
          };
        });
      }
    };
    const handleMouseUp = () => {
      unsetCursor();
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);
  }, []);

  const handlePostGrabMouseDown = useCallback(() => {
    const unsetCursor = setCursor('ew-resize');
    const handleMouseMove = (e: MouseEvent) => {
      const target = e.target as HTMLElement;
      if (target.matches('[data-word-index]')) {
        const targetRect = target.getBoundingClientRect();
        const targetWidth = targetRect.width;
        const mouseX = e.clientX - targetRect.left;
        let wordIndex = parseInt(target.dataset.wordIndex!);
        const isInRightHalfOfElement = mouseX > targetWidth / 2;
        if (isInRightHalfOfElement) {
          wordIndex += 1;
        }
        setReplacementConfirmation((prev) => {
          if (!prev) return prev;
          const prevEndIndex =
            prev.originalLyricsSplit[1].slice(-1)?.[0]?.index || Infinity;
          if (prevEndIndex === wordIndex) return prev;

          const allLyrics = prev.originalLyricsSplit.flat();
          const startIndex = Math.min(
            prev.originalLyricsSplit[1]?.[0]?.index || 0
          );
          wordIndex = Math.max(startIndex + 1, wordIndex);
          return {
            ...prev,
            originalLyricsSplit: [
              allLyrics.filter(({ index }) => index < startIndex),
              allLyrics.filter(
                ({ index }) => index >= startIndex && index < wordIndex
              ),
              allLyrics.filter(({ index }) => index >= wordIndex),
            ],
          };
        });
      }
    };
    const handleMouseUp = () => {
      unsetCursor();
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);
  }, []);

  if (!replacementConfirmation) return null;

  return (
    <GlassModule>
      <GlassModuleHeader>Current Lyrics</GlassModuleHeader>
      <GlassModuleBody>
        <Tooltip label='Ensure the selected span of lyrics matches what you hear in the selected portion of the song.'>
          <TextDisplay ref={textDisplayRef} data-lyrics-replacement='old'>
            <UnselectedLyrics>
              {replacementConfirmation.originalLyricsSplit[0].map(
                ({ lyric, index }, i) => (
                  <span key={i} data-word-index={index}>
                    {lyric.text}
                  </span>
                )
              )}
            </UnselectedLyrics>
            <GrabHandle onMouseDown={handlePreGrabMouseDown} />
            <SelectedLyrics ref={replacementRef}>
              {replacementConfirmation.originalLyricsSplit[1].map(
                ({ lyric, index }, i) => (
                  <span key={i} data-word-index={index}>
                    {lyric.text}
                  </span>
                )
              )}
            </SelectedLyrics>
            <GrabHandle
              onMouseDown={handlePostGrabMouseDown}
              style={{ transform: 'translateX(1px)' }}
            />
            <UnselectedLyrics>
              {replacementConfirmation.originalLyricsSplit[2].map(
                ({ lyric, index }, i) => (
                  <span key={i} data-word-index={index}>
                    {lyric.text}
                  </span>
                )
              )}
            </UnselectedLyrics>
          </TextDisplay>
        </Tooltip>
      </GlassModuleBody>
    </GlassModule>
  );
};

export const NewLyrics = () => {
  const { replacementConfirmation, setReplacementConfirmation } =
    useContext(AlignedLyricsContext);

  return (
    <GlassModule>
      <GlassModuleHeader>New Lyrics</GlassModuleHeader>
      <GlassModuleBody>
        <TextEditor
          onChange={(e) => {
            const value = e.currentTarget.value;
            setReplacementConfirmation((prev) =>
              !prev
                ? prev
                : {
                    ...prev,
                    replacementLyrics: value || '',
                  }
            );
          }}
          value={replacementConfirmation?.replacementLyrics}
        />
      </GlassModuleBody>
    </GlassModule>
  );
};
