import { Field, Label } from '@headlessui/react'
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 FieldError,
  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
  containerClassName?: 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,
    className,
    inputClassName,
    category,
    disabled = false,
    containerClassName,
  } = properties

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

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

  const watchContent = watch(name)
  const error: FieldError = 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'],
      }),
    ],
    immediatelyRender: false,
    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 (
    <Field
      className={twMerge('relative', containerClassName)}
      disabled={disabled}
      id={id}
    >
      {label && (
        <Label className={twMerge('mb-1 block text-gray-700', labelClassName)}>
          {label}{' '}
          {error && <span className="text-red-500">{error.message}</span>}
        </Label>
      )}

      {isPlainHTML ? (
        <EditorTextArea
          setIsPlainHTML={setIsPlainHTML}
          name={name}
          disabled={disabled}
        />
      ) : (
        <div className={twMerge('rounded-md', className)}>
          <EditorMenuBar
            disabled={disabled}
            category={category}
            editor={editor}
            setIsPlainHTML={setIsPlainHTML}
          />
          <EditorContent
            readOnly={disabled}
            editor={editor}
            id={id ?? generatedId}
            className={twMerge(
              'prose prose-headings:my-0 prose-p:my-0 max-h-96 max-w-none cursor-text overflow-y-auto rounded-b-md p-2',
              inputClassName,
            )}
            onClick={() => editor?.commands.focus()}
          />
        </div>
      )}
    </Field>
  )
}
