import { Color } from '@tiptap/extension-color'
import Highlight from '@tiptap/extension-highlight'
import Image from '@tiptap/extension-image'
import Link from '@tiptap/extension-link'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useEffect, useId, useState } from 'react'
import {
  get,
  type FieldValues,
  type Path,
  type RegisterOptions,
} from 'react-hook-form'
import { useRemixFormContext } from 'remix-hook-form'
import { twMerge } from 'tailwind-merge'

import { EditorMenuBar } from './editor-menubar'
import { EditorTextArea } from './editor-textarea'

type TProperties<TFormValues extends FieldValues> = {
  id?: string
  name: Path<TFormValues>
  label?: string
  placeholder?: string
  labelClassName?: string
  className?: string
  inputClassName?: string
  rules?: RegisterOptions
  disabled?: boolean
  isRequired?: boolean
  category: string
}

export const TextEditor = <TFormValues extends Record<string, unknown>>(
  properties: TProperties<TFormValues>,
) => {
  const {
    id,
    label,
    name,
    labelClassName,
    isRequired,
    className,
    inputClassName,
    category,
    disabled = false,
  } = properties

  const [isPlainHTML, setIsPlainHTML] = useState(false)
  const [init, setInit] = useState(true)
  const [darkMode, setDarkMode] = useState(false)
  const generatedId = useId()

  const {
    setValue,
    watch,
    formState: { errors },
  } = useRemixFormContext()

  const watchContent = watch(name)
  const error = get(errors, name)

  const editor = useEditor({
    editable: !disabled,
    extensions: [
      StarterKit,
      Highlight,
      Image.configure({
        inline: true,
      }),
      TextStyle,
      Color.configure({
        types: ['textStyle'],
      }),
      Link.configure({
        openOnClick: false,
      }),
      TextAlign.configure({
        types: ['heading', 'paragraph'],
      }),
    ],
    content: watchContent,
    onUpdate: ({ editor }) => {
      setValue(name, editor.getHTML() as any) // eslint-disable-line @typescript-eslint/no-explicit-any
    },
  })
  useEffect(() => {
    if (
      watchContent &&
      watchContent.length > 0 &&
      editor &&
      (isPlainHTML || init)
    ) {
      editor.commands.setContent(watchContent)
      setInit(false)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [watchContent])

  return (
    <div className={twMerge('', className)}>
      {label && (
        <label
          htmlFor={id ?? generatedId}
          className={twMerge('mb-2 block text-sm font-bold', labelClassName)}
        >
          {label}
          {isRequired && <sup className="text-[#DF0000]">*</sup>}
        </label>
      )}

      {isPlainHTML ? (
        <EditorTextArea
          setIsPlainHTML={setIsPlainHTML}
          name={name}
          disabled={disabled}
        />
      ) : (
        <>
          <EditorMenuBar
            disabled={disabled}
            category={category}
            editor={editor}
            setIsPlainHTML={setIsPlainHTML}
            darkMode={darkMode}
            setDarkMode={setDarkMode}
          />
          <EditorContent
            readOnly={disabled}
            editor={editor}
            id={id ?? generatedId}
            className={twMerge(
              'prose mb-1 max-h-96 max-w-none cursor-text overflow-y-auto rounded-[0_0_5px_5px] border border-[#D2D2D2] px-4 py-1',
              darkMode ? 'bg-[#00000055]' : '',
              inputClassName,
            )}
            onClick={() => editor?.commands.focus()}
          />
        </>
      )}

      {error && (
        <p className="text-xs text-[#DF0000] italic">
          {error?.message?.toString()}
        </p>
      )}
    </div>
  )
}
