import { createContext, useContext, useEffect } from 'react';

import { useEditor } from '@tiptap/react';

import useExtensions from './hooks/useExtensions';
import { type TextAreaContextType, type TextAreaProviderProps } from './types';
import {
  getTextAreaPlaceholderExtension,
  isActiveTextAreaEditor,
} from './utils/editor';

const TextAreaContext = createContext<TextAreaContextType | undefined>(
  undefined,
);

export const TextAreaHandlers = ({
  disabled = false,
  placeholder,
}: {
  disabled?: boolean;
  placeholder?: string;
}) => {
  const { editor } = useTextArea();

  useEffect(() => {
    if (!isActiveTextAreaEditor(editor)) return;
    editor.setEditable(!disabled);
  }, [disabled, editor]);

  useEffect(() => {
    if (placeholder === '' || !isActiveTextAreaEditor(editor)) return;

    const placeholderExtension = getTextAreaPlaceholderExtension(
      editor.extensionManager,
    );
    if (!placeholderExtension) return;

    placeholderExtension.options.placeholder = placeholder;
    editor.view.dispatch(editor.state.tr);
  }, [editor, placeholder]);

  return null;
};

export const TextAreaProvider = ({
  children,
  content,
  disabled = false,
  readOnly = false,
  handlePaste,
  handleDrop,
  onChange,
  onBlur,
  onFocus,
  placeholder,
  slotProps,
  immediatelyRender = true,
  variablesEnabled = false,
  characterLimit,
}: TextAreaProviderProps) => {
  const isEditable = !disabled && !readOnly;
  const extensions = useExtensions({
    placeholder,
    handlePaste,
    handleDrop,
    slotProps,
    variablesEnabled,
    characterLimit,
  });

  const editor = useEditor({
    extensions,
    content,
    editable: isEditable,
    onUpdate: ({ editor: currentEditor }) => {
      onChange?.(currentEditor.isEmpty ? '' : currentEditor.getHTML());
    },
    onBlur,
    onFocus,
    shouldRerenderOnTransaction: true,
    immediatelyRender,
  });

  return (
    <TextAreaContext.Provider value={{ editor, slotProps }}>
      {children}
      <TextAreaHandlers
        disabled={!isEditable}
        placeholder={placeholder}
      />
    </TextAreaContext.Provider>
  );
};

export const useTextArea = () => {
  const context = useContext(TextAreaContext);

  if (context === undefined) {
    throw new Error('useTextArea must be used within a TextAreaProvider');
  }

  return context;
};
