import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import type { Position, EditorStyleConfig, HistoryState } from '../lib'
import { cleanDom, HTMLEditor } from '../lib'

interface UseInjectModeOptions {
  styleConfig?: EditorStyleConfig
  enableGlobalContentEditable?: boolean
  onContentChange?: (srcDoc: string) => void
  onHistoryChange?: (state: HistoryState, editor: HTMLEditor) => void
  enabled?: boolean
}

const isValidDom = (innerText: string) => {
  try {
    const data = JSON.parse(innerText)
    if (data && !data.success) {
      return false
    }
  } catch {
    return true
  }
}

const isIfrmae = (target: HTMLElement) => {
  return target.tagName === 'IFRAME' || target instanceof HTMLIFrameElement
}

export interface UseInjectModeReturn {
  editor: HTMLEditor | null
  editorIns: HTMLEditor | null
  selectedElement: HTMLElement | null
  position: Position | null
  tipPosition: Position | null
  injectScript: (targetContainer: HTMLElement) => Promise<void>
  // 历史记录相关
  canUndo: boolean
  canRedo: boolean
  clearHistory: () => void
  loadSuccess: boolean
}

function waitForIframeReady(
  iframe: HTMLIFrameElement,
  timeout = 5000,
): Promise<void> {
  return new Promise(resolve => {
    const start = performance.now()

    const tryAttach = () => {
      const doc = iframe.contentDocument
      if (!doc) {
        if (performance.now() - start > timeout) return resolve()
        return requestAnimationFrame(tryAttach)
      }

      // 如果已经 complete，直接 resolve
      if (doc.readyState === 'complete') {
        return resolve()
      }

      let stableTimer: any
      const observer = new MutationObserver(() => {
        clearTimeout(stableTimer)
        stableTimer = setTimeout(() => {
          observer.disconnect()
          resolve()
        }, 800)
      })

      observer.observe(doc.documentElement, {
        childList: true,
        subtree: true,
        attributes: true,
      })

      // 超时保护
      setTimeout(() => {
        observer.disconnect()
        clearTimeout(stableTimer)
        console.error('iframe ready timeout')
        resolve()
      }, timeout)

      // 同时监听 readyState
      const checkReady = () => {
        if (doc.readyState === 'complete') {
          observer.disconnect()
          clearTimeout(stableTimer)
          resolve()
        } else if (performance.now() - start < timeout) {
          requestAnimationFrame(checkReady)
        }
      }
      checkReady()
    }

    tryAttach()
  })
}

export function useIframeMode(
  id: string,
  container: React.RefObject<HTMLIFrameElement | HTMLElement | null>,
  options?: UseInjectModeOptions,
  scale = 1,
): UseInjectModeReturn {
  const editorRef = useRef<HTMLEditor | null>(null)
  const [editorIns, setEditorIns] = useState<HTMLEditor | null>(null)
  const [selectedElement, setSelectedElement] = useState<HTMLElement | null>(
    null,
  )
  const [position, setPosition] = useState<Position | null>(null)
  const [canUndo, setCanUndo] = useState(false)
  const [canRedo, setCanRedo] = useState(false)
  const [loadSuccess, setLoadSuccess] = useState(false)

  const injectScript = useCallback(async (targetContainer: HTMLElement) => {
    if (!targetContainer) {
      console.error('ifrmae is null')
      return
    }
    if (editorRef.current) {
      console.error('ifrmae is destroyed')
      editorRef.current.destroy()
    }
    const editor = new HTMLEditor({
      id,
      styleConfig: options?.styleConfig,
      enableGlobalContentEditable: options?.enableGlobalContentEditable,
      onElementSelect: (element: HTMLElement | null, pos?: Position) => {
        setSelectedElement(element)
        if (element && pos) {
          setPosition(pos)
        } else {
          setPosition(null)
        }
      },
      onStyleChange: (element: HTMLElement) => {
        if (element) {
          const pos = element.getBoundingClientRect()
          setPosition(pos)
        }
      },
      onContentChange: () => {
        // 触发内容变化回调,数据清洗
        if (options?.onContentChange) {
          if (editor.isIframe) {
            const iframe = container.current as HTMLIFrameElement
            const iframeDoc = iframe.contentDocument?.documentElement
            if (iframeDoc) {
              const srcDoc = cleanDom(iframeDoc)
              options.onContentChange(srcDoc)
            }
          } else {
            const srcDoc = cleanDom(container.current as HTMLElement)
            options.onContentChange(srcDoc)
          }
        }
      },
      onHistoryChange: (state: HistoryState) => {
        setCanUndo(state.canUndo)
        setCanRedo(state.canRedo)
        options?.onHistoryChange?.(state, editor)
      },
      enableMoveable: true,
      helperBox: true,
      enableHistory: true,
      historyOptions: {
        maxHistorySize: 100,
        mergeInterval: 1000,
      },
    })
    editor.init(targetContainer)
    editorRef.current = editor
    setEditorIns(editor)
  }, [id, options, container])

  useEffect(() => {
    const element = container.current
    if (!element || options?.enabled === false) {
      return
    }
    const isIframeEle = isIfrmae(element)
    const handleKeyDown = (e: KeyboardEvent) => {
      // macos 快捷键 command + z 撤销 / command + shift + z 重做
      if (e.metaKey && e.key === 'z' && e.shiftKey) {
        e.preventDefault()
        editorRef.current?.redo()
      }
      if (e.metaKey && e.key === 'z' && !e.shiftKey) {
        e.preventDefault()
        editorRef.current?.undo()
      }
    }
    if (isIframeEle) {
      // iframe 加载
      const onLoad = async () => {
        await waitForIframeReady(element as HTMLIFrameElement)
        const doc = (element as HTMLIFrameElement).contentDocument!
        // 判断是否是合法的 iframe 内容
        const loadSuccess = isValidDom(doc.body.innerText)
        if (loadSuccess) {
          injectScript(doc.body)
          console.log('编辑器加载成功')
          setLoadSuccess(true)
          const document = (element as HTMLIFrameElement).contentDocument
            ?.documentElement
          document?.addEventListener('keydown', handleKeyDown)
          return () => {
            document?.removeEventListener('keydown', handleKeyDown)
          }
        }
      }
        ; (element as HTMLIFrameElement).onload = onLoad
    } else {
      injectScript(element)
      setLoadSuccess(true)
      document?.addEventListener('keydown', handleKeyDown)
      return () => {
        document?.removeEventListener('keydown', handleKeyDown)
      }
    }
  }, [injectScript, container, options?.enabled])

  useEffect(() => {
    return () => {
      if (editorRef.current && loadSuccess) {
        console.log('销毁编辑器')
        editorRef.current.destroy()
      }
    }
  }, [loadSuccess])

  const tipPosition = useMemo(() => {
    if (!container.current || !position) {
      return null
    }
    const elementRect = container.current.getBoundingClientRect()
    return {
      top: position.top * scale + elementRect.top,
      left: position.left * scale,
      width: position.width * scale,
      height: position.height * scale,
      bottom: position.bottom * scale + elementRect.top,
      right: position.right * scale + elementRect.left,
    }
  }, [position, scale, container])

  return {
    editor: editorRef.current,
    editorIns,
    selectedElement,
    position,
    tipPosition,
    injectScript,
    canUndo,
    canRedo,
    loadSuccess,
    clearHistory: () => editorRef.current?.clearHistory(),
  }
}
