import React, { useCallback, useEffect, useState, useImperativeHandle, forwardRef } from 'react';
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import {
  $getSelection,
  $isRangeSelection,
  FORMAT_TEXT_COMMAND,
  UNDO_COMMAND,
  REDO_COMMAND,
  $createParagraphNode,
  $getRoot,
  CLEAR_EDITOR_COMMAND,
  $createTextNode,
  type LexicalEditor,
} from 'lexical';
import { $isLinkNode, TOGGLE_LINK_COMMAND, LinkNode } from '@lexical/link';
import {
  INSERT_ORDERED_LIST_COMMAND,
  INSERT_UNORDERED_LIST_COMMAND,
  REMOVE_LIST_COMMAND,
  ListNode,
  ListItemNode,
} from '@lexical/list';
import {
  $createHeadingNode,
  $isHeadingNode,
  HeadingNode,
  type HeadingTagType,
  $createQuoteNode,
  $isQuoteNode,
  QuoteNode,
} from '@lexical/rich-text';
import { $setBlocksType } from '@lexical/selection';
import { $getNearestNodeOfType } from '@lexical/utils';
import { type InitialConfigType, LexicalComposer } from '@lexical/react/LexicalComposer';
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
import { ClearEditorPlugin } from '@lexical/react/LexicalClearEditorPlugin';
import { $generateNodesFromDOM, $generateHtmlFromNodes } from '@lexical/html';
import type { EditorState } from 'lexical';
import BoldIcon from '../../assets/icons/bold.svg?react';
import ItalicIcon from '../../assets/icons/italic.svg?react';
import LinkIcon from '../../assets/icons/link.svg?react';
import BulletedListIcon from '../../assets/icons/bulletedlist.svg?react';
import NumberedListIcon from '../../assets/icons/numberedlist.svg?react';
import UndoIcon from '../../assets/icons/undo.svg?react';
import RedoIcon from '../../assets/icons/redo.svg?react';
import QuoteIcon from '../../assets/icons/blockquote.svg?react';
import RemoveFormatIcon from '../../assets/icons/remove-format.svg?react';
import './TextEditor.css';

type BlockType = 'paragraph' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'quote';

export interface TextEditorRef {
  clear: () => void;
  setText: (value: string) => void;
  getText: () => string;
}

interface TextEditorProps {
  floatingMenu?: React.ReactNode;
  overlay?: React.ReactNode;
  labelId?: string;
  initialText?: string;
  onChange?: (text: string) => void;
}

function ToolbarPlugin() {
  const [editor] = useLexicalComposerContext();
  const [isBold, setIsBold] = useState<boolean>(false);
  const [isItalic, setIsItalic] = useState<boolean>(false);
  const [isLink, setIsLink] = useState<boolean>(false);
  const [isBulletList, setIsBulletList] = useState<boolean>(false);
  const [isNumberedList, setIsNumberedList] = useState<boolean>(false);
  const [blockType, setBlockType] = useState<BlockType>('paragraph');

  const updateToolbar = useCallback(() => {
    const selection = $getSelection();
    if ($isRangeSelection(selection)) {
      setIsBold(selection.hasFormat('bold'));
      setIsItalic(selection.hasFormat('italic'));

      // Check if selection is inside a link
      const node = selection.anchor.getNode();
      const parent = node.getParent();
      setIsLink($isLinkNode(parent) || $isLinkNode(node));

      // Check for list types
      const anchorNode = selection.anchor.getNode();
      const element =
        anchorNode.getKey() === 'root' ? anchorNode : anchorNode.getTopLevelElementOrThrow();
      const elementDOM = editor.getElementByKey(element.getKey());

      if (elementDOM !== null) {
        const listNode = $getNearestNodeOfType(anchorNode, ListNode);
        const type = listNode ? listNode.getListType() : null;
        setIsBulletList(type === 'bullet');
        setIsNumberedList(type === 'number');
      }

      // Check block type.
      if ($isHeadingNode(element)) {
        const tag = element.getTag();
        setBlockType(tag as BlockType);
      } else if ($isQuoteNode(element)) {
        setBlockType('quote');
      } else {
        setBlockType('paragraph');
      }
    }
  }, [editor]);

  useEffect(() => {
    return editor.registerUpdateListener(({ editorState }) => {
      editorState.read(() => {
        updateToolbar();
      });
    });
  }, [editor, updateToolbar]);

  const insertLink = useCallback(() => {
    if (!isLink) {
      const url = prompt('Enter URL:');
      if (url) {
        editor.dispatchCommand(TOGGLE_LINK_COMMAND, url);
      }
    } else {
      editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
    }
  }, [editor, isLink]);

  const formatBulletList = () => {
    if (isBulletList) {
      editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
    } else {
      editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
    }
  };

  const formatNumberedList = () => {
    if (isNumberedList) {
      editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
    } else {
      editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
    }
  };

  const formatBlock = (type: BlockType) => {
    editor.update(() => {
      const selection = $getSelection();
      if ($isRangeSelection(selection)) {
        if (type === 'paragraph') {
          $setBlocksType(selection, () => $createParagraphNode());
        } else if (type === 'quote') {
          $setBlocksType(selection, () => $createQuoteNode());
        } else {
          $setBlocksType(selection, () => $createHeadingNode(type as HeadingTagType));
        }
      }
    });
  };

  const clearFormatting = useCallback(() => {
    editor.update(() => {
      const selection = $getSelection();
      if ($isRangeSelection(selection)) {
        // Remove all text formatting
        if (selection.hasFormat('bold')) {
          selection.formatText('bold');
        }
        if (selection.hasFormat('italic')) {
          selection.formatText('italic');
        }
        if (selection.hasFormat('strikethrough')) {
          selection.formatText('strikethrough');
        }
        if (selection.hasFormat('subscript')) {
          selection.formatText('subscript');
        }
        if (selection.hasFormat('superscript')) {
          selection.formatText('superscript');
        }
        if (selection.hasFormat('code')) {
          selection.formatText('code');
        }

        // Remove links
        const nodes = selection.getNodes();
        nodes.forEach((node) => {
          const parent = node.getParent();
          if ($isLinkNode(parent)) {
            const children = parent.getChildren();
            children.forEach((child) => {
              parent.insertBefore(child);
            });
            parent.remove();
          } else if ($isLinkNode(node)) {
            const children = node.getChildren();
            children.forEach((child) => {
              node.insertBefore(child);
            });
            node.remove();
          }
        });

        // Convert to paragraph if it's a heading or quote
        const anchorNode = selection.anchor.getNode();
        const element =
          anchorNode.getKey() === 'root' ? anchorNode : anchorNode.getTopLevelElementOrThrow();

        if ($isHeadingNode(element) || $isQuoteNode(element)) {
          $setBlocksType(selection, () => $createParagraphNode());
        }

        // Remove list formatting
        const listNode = $getNearestNodeOfType(anchorNode, ListNode);
        if (listNode) {
          editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
        }
      }
    });
  }, [editor]);

  return (
    <div className="text-editor__toolbar">
      <button
        onClick={() => editor.dispatchCommand(UNDO_COMMAND, undefined)}
        className="text-editor__button"
        type="button"
        title="Undo"
      >
        <UndoIcon />
      </button>
      <button
        onClick={() => editor.dispatchCommand(REDO_COMMAND, undefined)}
        className="text-editor__button"
        type="button"
        title="Redo"
      >
        <RedoIcon />
      </button>
      <div className="text-editor__divider"></div>
      <select
        value={blockType}
        onChange={(e) => formatBlock(e.target.value as BlockType)}
        className="text-editor__select"
        aria-label="Select block formatting"
      >
        <option value="paragraph">Paragraph</option>
        <option value="h1">Heading 1</option>
        <option value="h2">Heading 2</option>
        <option value="h3">Heading 3</option>
        <option value="h4">Heading 4</option>
        <option value="h5">Heading 5</option>
        <option value="h6">Heading 6</option>
        <option value="quote">Quote</option>
      </select>
      <div className="text-editor__divider"></div>
      <button
        onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'bold')}
        className={`text-editor__button ${isBold ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Bold"
      >
        <BoldIcon />
      </button>
      <button
        onClick={() => editor.dispatchCommand(FORMAT_TEXT_COMMAND, 'italic')}
        className={`text-editor__button ${isItalic ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Italic"
      >
        <ItalicIcon />
      </button>
      <button
        onClick={insertLink}
        className={`text-editor__button ${isLink ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Link"
      >
        <LinkIcon />
      </button>
      <button
        onClick={formatBulletList}
        className={`text-editor__button ${isBulletList ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Bulleted List"
      >
        <BulletedListIcon />
      </button>
      <button
        onClick={formatNumberedList}
        className={`text-editor__button ${isNumberedList ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Numbered List"
      >
        <NumberedListIcon />
      </button>
      <button
        onClick={() => formatBlock(blockType === 'quote' ? 'paragraph' : 'quote')}
        className={`text-editor__button ${blockType === 'quote' ? 'text-editor__button--active' : ''}`}
        type="button"
        title="Quote"
      >
        <QuoteIcon />
      </button>
      <button
        onClick={clearFormatting}
        className="text-editor__button"
        type="button"
        title="Remove Formatting"
      >
        <RemoveFormatIcon />
      </button>
    </div>
  );
}

function InitialTextPlugin({ initialText }: { initialText: string | undefined }) {
  const [editor] = useLexicalComposerContext();
  const [hasInitialized, setHasInitialized] = useState(false);

  useEffect(() => {
    if (!hasInitialized && initialText !== undefined) {
      editor.update(() => {
        const root = $getRoot();
        root.clear();

        // Check if it's HTML
        const isHTML = /<[a-z][\s\S]*>/i.test(initialText);

        if (isHTML) {
          const parser = new DOMParser();
          const dom = parser.parseFromString(initialText, 'text/html');
          const nodes = $generateNodesFromDOM(editor, dom);
          root.append(...nodes);
        } else {
          const paragraph = $createParagraphNode();
          const textNode = $createTextNode(initialText);
          paragraph.append(textNode);
          root.append(paragraph);
        }
      });
      setHasInitialized(true);
    }
  }, [editor, initialText, hasInitialized]);

  return null;
}

interface EditorControlPluginProps {
  clearTrigger: number;
  setTextTrigger: { value: string; timestamp: number } | null;
}

function EditorControlPlugin({ clearTrigger, setTextTrigger }: EditorControlPluginProps) {
  const [editor] = useLexicalComposerContext();

  useEffect(() => {
    if (clearTrigger > 0) {
      editor.dispatchCommand(CLEAR_EDITOR_COMMAND, undefined);
    }
  }, [editor, clearTrigger]);

  useEffect(() => {
    if (setTextTrigger) {
      editor.update(() => {
        const root = $getRoot();
        root.clear();

        // Check if it's HTML
        const isHTML = /<[a-z][\s\S]*>/i.test(setTextTrigger.value);

        if (isHTML) {
          const parser = new DOMParser();
          const dom = parser.parseFromString(setTextTrigger.value, 'text/html');
          const nodes = $generateNodesFromDOM(editor, dom);
          root.append(...nodes);
        } else {
          const paragraph = $createParagraphNode();
          const textNode = $createTextNode(setTextTrigger.value);
          paragraph.append(textNode);
          root.append(paragraph);
        }
      });
    }
  }, [editor, setTextTrigger]);

  return <ClearEditorPlugin />;
}

const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(({ floatingMenu = <></>, overlay = <>

    </>, labelId, initialText, onChange }, ref) => {
  const [clearTrigger, setClearTrigger] = useState(0);
  const [setTextTrigger, setSetTextTrigger] = useState<{
    value: string;
    timestamp: number;
  } | null>(null);
  const [currentHtml, setCurrentHtml] = useState<string>('');

  useImperativeHandle(ref, () => ({
    clear: () => {
      setClearTrigger((prev) => prev + 1);
    },
    setText: (value: string) => {
      setSetTextTrigger({ value, timestamp: Date.now() });
    },
    getText: () => {
      return currentHtml;
    },
  }));

  const initialConfig: InitialConfigType = {
    namespace: 'ConductorEditor',
    onError: (error: Error) => console.error(error),
    nodes: [HeadingNode, QuoteNode, LinkNode, ListNode, ListItemNode],
  };

  const handleEditorChange = useCallback(
    (editorState: EditorState, editor: LexicalEditor) => {
      editorState.read(() => {
        const htmlString = $generateHtmlFromNodes(editor, null);

        // Strip inline styles and unnecessary spans
        const parser = new DOMParser();
        const doc = parser.parseFromString(htmlString, 'text/html');

        // Remove all style attributes
        const elementsWithStyle = doc.querySelectorAll('[style]');
        elementsWithStyle.forEach((element) => {
          element.removeAttribute('style');
        });

        // Remove all spans by replacing them with their content
        const spans = Array.from(doc.querySelectorAll('span'));
        spans.forEach((span) => {
          const parent = span.parentNode;
          if (parent) {
            while (span.firstChild) {
              parent.insertBefore(span.firstChild, span);
            }
            parent.removeChild(span);
          }
        });

        const cleanedHtml = doc.body.innerHTML;
        setCurrentHtml(cleanedHtml);

        if (onChange) {
          onChange(cleanedHtml);
        }
      });
    },
    [onChange],
  );

  return (
    <LexicalComposer initialConfig={initialConfig}>
      <div className="text-editor">
        <ToolbarPlugin />
        {floatingMenu}
        <RichTextPlugin
          contentEditable={
            <ContentEditable className="text-editor__content" aria-labelledby={labelId} />
          }
          ErrorBoundary={LexicalErrorBoundary}
        />
        {overlay && <div className="text-editor__overlay">{overlay}</div>}
        <HistoryPlugin />
        <ListPlugin />
        <LinkPlugin />
        <OnChangePlugin onChange={handleEditorChange} />
        {initialText !== undefined && <InitialTextPlugin initialText={initialText} />}
        <EditorControlPlugin clearTrigger={clearTrigger} setTextTrigger={setTextTrigger} />
      </div>
    </LexicalComposer>
  );
});

TextEditor.displayName = 'TextEditor';

export default TextEditor;
