import { useRef, useEffect, useState, useCallback } from 'react'
import { Bold, Italic, Underline, List, ListOrdered, ImagePlus } from 'lucide-react'
import { uploadMediaFiles } from '../services/mediaService'
import styles from './RichTextEditor.module.css'

interface RichTextEditorProps {
  value: string
  onChange: (value: string) => void
  placeholder?: string
  allowImages?: boolean
  editorMinHeight?: number
}

const SELECTED_IMAGE_CLASS = 'richTextImageSelected'

export function RichTextEditor({
  value,
  onChange,
  placeholder,
  allowImages = false,
  editorMinHeight = 140,
}: RichTextEditorProps) {
  const editorShellRef = useRef<HTMLDivElement>(null)
  const editorRef = useRef<HTMLDivElement>(null)
  const fileInputRef = useRef<HTMLInputElement>(null)
  const dragRef = useRef<{ startX: number; startWidth: number; img: HTMLImageElement } | null>(
    null,
  )
  const [uploadingImage, setUploadingImage] = useState(false)
  const [uploadError, setUploadError] = useState('')
  const [selectedImageEl, setSelectedImageEl] = useState<HTMLImageElement | null>(null)
  const [handlePos, setHandlePos] = useState<{ top: number; left: number } | null>(null)

  const syncChange = useCallback(() => {
    if (editorRef.current) {
      onChange(editorRef.current.innerHTML)
    }
  }, [onChange])

  const updateHandlePosition = useCallback((img: HTMLImageElement | null) => {
    if (!img || !editorShellRef.current) {
      setHandlePos(null)
      return
    }

    const shellRect = editorShellRef.current.getBoundingClientRect()
    const imgRect = img.getBoundingClientRect()
    setHandlePos({
      top: imgRect.bottom - shellRect.top - 6,
      left: imgRect.right - shellRect.left - 6,
    })
  }, [])

  const clearImageSelection = useCallback(() => {
    editorRef.current?.querySelectorAll(`img.${SELECTED_IMAGE_CLASS}`).forEach((img) => {
      img.classList.remove(SELECTED_IMAGE_CLASS)
    })
    setSelectedImageEl(null)
    setHandlePos(null)
  }, [])

  const selectImage = useCallback(
    (img: HTMLImageElement) => {
      editorRef.current?.querySelectorAll(`img.${SELECTED_IMAGE_CLASS}`).forEach((node) => {
        node.classList.remove(SELECTED_IMAGE_CLASS)
      })
      img.classList.add(SELECTED_IMAGE_CLASS)
      setSelectedImageEl(img)
      updateHandlePosition(img)
    },
    [updateHandlePosition],
  )

  useEffect(() => {
    if (editorRef.current && editorRef.current.innerHTML !== value) {
      editorRef.current.innerHTML = value
      clearImageSelection()
    }
  }, [value, clearImageSelection])

  useEffect(() => {
    if (!allowImages) return

    const editor = editorRef.current
    if (!editor) return

    function onEditorClick(e: MouseEvent) {
      const target = e.target
      if (!(target instanceof HTMLImageElement) || !editor!.contains(target)) return

      e.preventDefault()
      selectImage(target)
    }

    function onDocumentClick(e: MouseEvent) {
      const target = e.target as Node
      if (editor!.contains(target)) return
      if (target instanceof Element && target.closest(`.${styles.resizeHandle}`)) return
      clearImageSelection()
    }

    editor.addEventListener('click', onEditorClick)
    document.addEventListener('click', onDocumentClick)
    return () => {
      editor.removeEventListener('click', onEditorClick)
      document.removeEventListener('click', onDocumentClick)
    }
  }, [allowImages, selectImage, clearImageSelection])

  useEffect(() => {
    if (!selectedImageEl) return

    function refreshHandle() {
      if (selectedImageEl?.isConnected) {
        updateHandlePosition(selectedImageEl)
      }
    }

    window.addEventListener('resize', refreshHandle)
    editorRef.current?.addEventListener('scroll', refreshHandle)
    return () => {
      window.removeEventListener('resize', refreshHandle)
      editorRef.current?.removeEventListener('scroll', refreshHandle)
    }
  }, [selectedImageEl, updateHandlePosition])

  function exec(cmd: string, arg?: string) {
    document.execCommand(cmd, false, arg)
    editorRef.current?.focus()
    syncChange()
  }

  function styleInsertedImage(img: HTMLImageElement) {
    img.style.width = '100%'
    img.style.maxWidth = '100%'
    img.style.height = 'auto'
    img.draggable = false
  }

  async function handleImageSelect(e: React.ChangeEvent<HTMLInputElement>) {
    const file = e.target.files?.[0]
    e.target.value = ''
    if (!file || !editorRef.current) return

    setUploadingImage(true)
    setUploadError('')

    try {
      const uploaded = await uploadMediaFiles([file])
      const url = uploaded[0]?.publicUrl
      if (!url) throw new Error('Image upload failed.')

      editorRef.current.focus()
      document.execCommand('insertImage', false, url)

      const imgs = editorRef.current.querySelectorAll('img')
      const lastImg = imgs[imgs.length - 1]
      if (lastImg instanceof HTMLImageElement) {
        styleInsertedImage(lastImg)
        selectImage(lastImg)
      }

      syncChange()
    } catch (err) {
      setUploadError(err instanceof Error ? err.message : 'Unable to upload image.')
    } finally {
      setUploadingImage(false)
    }
  }

  function onResizeStart(e: React.MouseEvent) {
    if (!selectedImageEl || !editorRef.current) return

    e.preventDefault()
    e.stopPropagation()

    dragRef.current = {
      startX: e.clientX,
      startWidth: selectedImageEl.offsetWidth,
      img: selectedImageEl,
    }

    function onMove(ev: MouseEvent) {
      if (!dragRef.current || !editorRef.current) return
      const delta = ev.clientX - dragRef.current.startX
      const maxWidth = editorRef.current.clientWidth - 16
      const nextWidth = Math.max(80, Math.min(maxWidth, dragRef.current.startWidth + delta))
      dragRef.current.img.style.width = `${nextWidth}px`
      dragRef.current.img.style.height = 'auto'
      dragRef.current.img.style.maxWidth = '100%'
      updateHandlePosition(dragRef.current.img)
    }

    function onUp() {
      dragRef.current = null
      syncChange()
      document.removeEventListener('mousemove', onMove)
      document.removeEventListener('mouseup', onUp)
    }

    document.addEventListener('mousemove', onMove)
    document.addEventListener('mouseup', onUp)
  }

  return (
    <div className={styles.wrapper}>
      <div className={styles.toolbar}>
        <button type="button" onClick={() => exec('bold')} title="Bold" aria-label="Bold">
          <Bold size={16} />
        </button>
        <button type="button" onClick={() => exec('italic')} title="Italic" aria-label="Italic">
          <Italic size={16} />
        </button>
        <button type="button" onClick={() => exec('underline')} title="Underline" aria-label="Underline">
          <Underline size={16} />
        </button>
        <span className={styles.divider} />
        <button type="button" onClick={() => exec('insertUnorderedList')} title="Bullet list" aria-label="Bullet list">
          <List size={16} />
        </button>
        <button type="button" onClick={() => exec('insertOrderedList')} title="Numbered list" aria-label="Numbered list">
          <ListOrdered size={16} />
        </button>
        {allowImages && (
          <>
            <span className={styles.divider} />
            <button
              type="button"
              onClick={() => fileInputRef.current?.click()}
              disabled={uploadingImage}
              title="Insert image"
              aria-label="Insert image"
            >
              <ImagePlus size={16} />
            </button>
            <input
              ref={fileInputRef}
              type="file"
              accept="image/*"
              hidden
              onChange={handleImageSelect}
            />
          </>
        )}
      </div>
      {uploadError && (
        <p className={styles.uploadError} role="alert">
          {uploadError}
        </p>
      )}
      <div ref={editorShellRef} className={styles.editorShell}>
        <div
          ref={editorRef}
          className={styles.editor}
          style={{ minHeight: editorMinHeight }}
          contentEditable
          role="textbox"
          aria-multiline="true"
          data-placeholder={placeholder}
          onInput={syncChange}
          suppressContentEditableWarning
        />
        {allowImages && selectedImageEl && handlePos && (
          <button
            type="button"
            className={styles.resizeHandle}
            style={{ top: handlePos.top, left: handlePos.left }}
            onMouseDown={onResizeStart}
            aria-label="Resize image"
          />
        )}
      </div>
    </div>
  )
}
