import styled from '@emotion/styled';
import { InitialConfigType } from '@lexical/react/LexicalComposer';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin';
import { EditorThemeClasses } from 'lexical';
import { memo, useCallback, useRef } from 'react';

import OverscrollFade from './OverscrollFade';
import {
  SectionHeadingNode,
  SectionHeadingPlugin,
} from './sectionHeadingPlugin';
import {
  SINGLE_LINE_SELECTION_HEIGHT,
  SelectionWatcherPlugin,
} from './selectionWatcherPlugin';

const theme: EditorThemeClasses = {
  paragraph: '.editor-paragraph',
};

// Catch any errors that occur during Lexical updates and log them
// or throw them as needed. If you don't throw them, Lexical will
// try to recover gracefully without losing user data.
function onError(error: Error) {
  console.error(error);
}

const Wrapper = styled.div`
  position: relative;
  min-height: 0;
  color: rgb(var(--rgb-foreground-primary));
`;

const ScrollWrapper = styled.div`
  padding: 20px 0 60px 0;
  overflow-y: scroll;
  height: 100%;
  > [contenteditable] {
    width: 100%;
    min-height: 100%;
    &:focus {
      outline: none;
    }
  }
  .section-heading {
    opacity: 0.5;
  }
`;

const Placeholder = styled.div`
  position: absolute;
  top: 20px;
  left: 0;
  opacity: 50%;
  pointer-events: none;
  color: rgb(var(--rgb-foreground-tertiary));
`;

export const initialConfig: InitialConfigType = {
  namespace: 'LyricsEditor',
  theme,
  onError,
  nodes: [SectionHeadingNode],
};

const placeholder = <Placeholder>Enter some text...</Placeholder>;

export default memo(function LexicalTextarea({
  onSelectionUpdate,
}: {
  onSelectionUpdate: (bounds: DOMRect, scrollTop: number) => void;
}) {
  const relativeSelectionRectRef = useRef<DOMRect>(
    new DOMRect(0, 0, 0, SINGLE_LINE_SELECTION_HEIGHT)
  );
  const scrollTopRef = useRef(0);

  const handleSelectionUpdate = useCallback(() => {
    onSelectionUpdate(relativeSelectionRectRef.current, scrollTopRef.current);
  }, [onSelectionUpdate]);

  const receiveRelativeSelectionRect = useCallback(
    (rect: DOMRect) => {
      relativeSelectionRectRef.current = rect;
      handleSelectionUpdate();
    },
    [handleSelectionUpdate]
  );

  const handleScroll = useCallback(
    (scrollTop: number) => {
      scrollTopRef.current = scrollTop;
      handleSelectionUpdate();
    },
    [handleSelectionUpdate]
  );

  return (
    <Wrapper>
      <ScrollWrapper>
        <PlainTextPlugin
          contentEditable={<ContentEditable />}
          placeholder={placeholder}
          ErrorBoundary={LexicalErrorBoundary}
        />
        <HistoryPlugin />
        <SectionHeadingPlugin />
        <SelectionWatcherPlugin
          onSelectionUpdate={receiveRelativeSelectionRect}
          onScroll={handleScroll}
        />
      </ScrollWrapper>
      <OverscrollFade
        above={true}
        height={0}
        fadeHeight={30}
        color='rgb(var(--rgb-background-secondary))'
      />
      <OverscrollFade
        above={false}
        height={0}
        fadeHeight={30}
        color='rgb(var(--rgb-background-secondary))'
      />
    </Wrapper>
  );
});
