/* eslint-disable no-param-reassign */
import React, {
  ClipboardEventHandler,
  KeyboardEventHandler,
  ReactNode,
  forwardRef,
  useCallback,
  useState,
} from 'react';
import { Box, Scroll, Text } from 'folds';
import { Descendant, Editor, createEditor, Element as SlateElement, Text as SlateText } from 'slate';
import {
  Slate,
  Editable,
  withReact,
  RenderLeafProps,
  RenderElementProps,
  RenderPlaceholderProps,
} from 'slate-react';
import { withHistory } from 'slate-history';
import { BlockType } from './types';
import { RenderElement, RenderLeaf } from './Elements';
import { CustomElement } from './slate';
import * as css from './Editor.css';
import { toggleKeyboardShortcut } from './keyboard';
import { createCommandElement } from './utils';

const initialValue: CustomElement[] = [
  {
    type: BlockType.Paragraph,
    children: [{ text: '' }],
  },
];

const withInline = (editor: Editor): Editor => {
  const { isInline } = editor;

  editor.isInline = (element) =>
    [BlockType.Mention, BlockType.Emoticon, BlockType.Link, BlockType.Command].includes(
      element.type
    ) || isInline(element);

  return editor;
};

const withVoid = (editor: Editor): Editor => {
  const { isVoid } = editor;

  editor.isVoid = (element) =>
    [BlockType.Mention, BlockType.Emoticon, BlockType.Command].includes(element.type) ||
    isVoid(element);

  return editor;
};

type CommandValidatorFn = (commandName: string) => boolean;

let commandValidator: CommandValidatorFn | null = null;

export const setCommandValidator = (validator: CommandValidatorFn) => {
  commandValidator = validator;
};

const withCommandAutoConvert = (editor: Editor): Editor => {
  // Removed auto-conversion on space to prevent focus issues
  // Commands are now highlighted visually via decorations
  // and executed when Enter is pressed
  return editor;
};

export const useEditor = (): Editor => {
  const [editor] = useState(() => 
    withCommandAutoConvert(withInline(withVoid(withReact(withHistory(createEditor())))))
  );
  return editor;
};

export type EditorChangeHandler = (value: Descendant[]) => void;
type CustomEditorProps = {
  editableName?: string;
  top?: ReactNode;
  bottom?: ReactNode;
  before?: ReactNode;
  after?: ReactNode;
  maxHeight?: string;
  /** Stretch the editable area to fill the parent (e.g. post modal). */
  fillHeight?: boolean;
  editor: Editor;
  placeholder?: string;
  onKeyDown?: KeyboardEventHandler;
  onKeyUp?: KeyboardEventHandler;
  onChange?: EditorChangeHandler;
  onPaste?: ClipboardEventHandler;
};
export const CustomEditor = forwardRef<HTMLDivElement, CustomEditorProps>(
  (
    {
      editableName,
      top,
      bottom,
      before,
      after,
      maxHeight = '50vh',
      fillHeight = false,
      editor,
      placeholder,
      onKeyDown,
      onKeyUp,
      onChange,
      onPaste,
    },
    ref
  ) => {
    const renderElement = useCallback(
      (props: RenderElementProps) => <RenderElement {...props} />,
      []
    );

    const renderLeaf = useCallback((props: RenderLeafProps) => <RenderLeaf {...props} />, []);

    const decorate = useCallback(([node, path]: [any, number[]]) => {
      const ranges: any[] = [];
      
      // Only decorate text nodes in the first paragraph
      if (
        path.length === 2 &&
        path[0] === 0 &&
        path[1] === 0 &&
        SlateText.isText(node)
      ) {
        const firstChild = editor.children[0];
        
        if (SlateElement.isElement(firstChild) && firstChild.type === BlockType.Paragraph) {
          const [firstInline, secondInline] = firstChild.children;
          
          // Don't decorate if we already have a CommandElement
          if (SlateElement.isElement(secondInline) && secondInline.type === BlockType.Command) {
            return ranges;
          }
          
          // Check if the text matches /command pattern
          const text = node.text;
          const match = text.match(/^(\s*\/\w+)/);
          
          if (match && commandValidator) {
            const commandText = match[1].trim();
            const commandName = commandText.substring(1);
            
            if (commandValidator(commandName)) {
              // Create decoration for the command text
              ranges.push({
                anchor: { path, offset: 0 },
                focus: { path, offset: match[1].length },
                pendingCommand: true,
              });
            }
          }
        }
      }
      
      return ranges;
    }, [editor]);

    const handleKeydown: KeyboardEventHandler = useCallback(
      (evt) => {
        onKeyDown?.(evt);
        const shortcutToggled = toggleKeyboardShortcut(editor, evt);
        if (shortcutToggled) evt.preventDefault();
      },
      [editor, onKeyDown]
    );

    const renderPlaceholder = useCallback(
      ({ attributes, children }: RenderPlaceholderProps) => (
        <span {...attributes} className={css.EditorPlaceholderContainer}>
          {/* Inner component to style the actual text position and appearance */}
          <Text as="span" className={css.EditorPlaceholderTextVisual} truncate>
            {children}
          </Text>
        </span>
      ),
      []
    );

    const scrollStyle = fillHeight
      ? { flex: '1 1 auto', minHeight: maxHeight, height: '100%' }
      : { maxHeight };

    return (
      <div
        className={css.Editor}
        ref={ref}
        style={
          fillHeight
            ? { display: 'flex', flexDirection: 'column', height: '100%', minHeight: maxHeight }
            : undefined
        }
      >
        <Slate editor={editor} initialValue={initialValue} onChange={onChange}>
          {top}
          <Box
            alignItems={fillHeight ? 'Start' : 'Center'}
            grow={fillHeight ? 'Yes' : undefined}
            style={fillHeight ? { width: '100%', minHeight: 0, flex: '1 1 auto' } : undefined}
          >
            {before && (
              <Box className={css.EditorOptions} alignItems="Center" gap="100" shrink="No">
                {before}
              </Box>
            )}
            <Scroll
              className={css.EditorTextareaScroll}
              variant="SurfaceVariant"
              style={scrollStyle}
              size="0"
              visibility="Hover"
              hideTrack
            >
              <Editable
                data-editable-name={editableName}
                className={css.EditorTextarea}
                placeholder={placeholder}
                renderPlaceholder={renderPlaceholder}
                renderElement={renderElement}
                renderLeaf={renderLeaf}
                decorate={decorate}
                onKeyDown={handleKeydown}
                onKeyUp={onKeyUp}
                onPaste={onPaste}
              />
            </Scroll>
            {after && (
              <Box className={css.EditorOptions} alignItems="Center" gap="100" shrink="No">
                {after}
              </Box>
            )}
          </Box>
          {bottom}
        </Slate>
      </div>
    );
  }
);
