import React, { useEffect, useRef } from 'react'
import { cn } from '@/utils/cn'
import { type SceneElement } from './domino'
import { DOMINO_EL_PREFIX } from './constants'
import {
  useDominoStore,
  useDominoStoreInstance,
  useDominoRenderHooks,
} from './domino-hooks'

import { MemoizedElementContent } from './element-content'

export interface ElementRendererProps {
  id: string
}

/**
 * 统一交互外壳：处理坐标、选中、悬停、焦点、递归
 */
export const ElementRenderer: React.FC<ElementRendererProps> = React.memo(
  function ElementRenderer({ id }) {
    const store = useDominoStoreInstance()
    const element = useDominoStore(
      state => state.elements[id] || state.placeholders[id],
    )
    const isFocused = useDominoStore(state => state.focusedElementId === id)
    const setHoveredElementId = useDominoStore(
      state => state.setHoveredElementId,
    )
    const setFocusedElementId = useDominoStore(
      state => state.setFocusedElementId,
    )

    const { renderElement } = useDominoRenderHooks()

    const elementRef = useRef<HTMLDivElement>(null)

    useEffect(() => {
      // For text elements, we want the inner contentEditable to have focus, not the wrapper
      if (!element || element?.type === 'text') return

      if (isFocused && document.activeElement !== elementRef.current) {
        elementRef.current?.focus({ preventScroll: true })
      }
    }, [isFocused, element?.type])

    if (!element) return null

    // 1. 统一位置样式
    const style: React.CSSProperties = {
      width: element.type === 'text' && !element.width ? 'auto' : element.width,
      height: element.type === 'text' ? 'auto' : element.height,
      minHeight: element.type === 'text' ? element.height : undefined,
      minWidth: element.type === 'text' && !element.width ? 20 : undefined,
      transform: `translate3d(${element.x}px, ${element.y}px, 0) rotate(${element.rotation}deg)`,
      overflow: 'visible',
      zIndex: element.type === 'artboard' ? -1 : undefined,
    }

    // 2. 统一交互逻辑
    const handleFocus = (e: React.FocusEvent) => {
      e.stopPropagation()
      if (store.getState().focusedElementId === id) return
      setFocusedElementId(id)
    }

    const handleClick = (e: React.MouseEvent) => {
      e.stopPropagation()

      // If text is already focused, bale out to allow native click behavior (move cursor)
      if (element.type === 'text' && isFocused) {
        return
      }

      // If clicking non-text, clear focus (Interactions took care of selection)
      if (element.type !== 'text') {
        setFocusedElementId(null)
      }
    }

    const handleDoubleClick = (e: React.MouseEvent) => {
      e.stopPropagation()
      if (element.type === 'text') {
        setFocusedElementId(id)
      }
    }

    const defaultContent = <MemoizedElementContent element={element} />
    const content = renderElement
      ? renderElement({ element, defaultRender: defaultContent })
      : defaultContent

    return (
      <div
        id={DOMINO_EL_PREFIX + id}
        ref={elementRef}
        tabIndex={element.type === 'text' ? undefined : 0}
        className={cn(
          'domino-element absolute flex flex-col pointer-events-auto box-border',

          element.type === 'text'
            ? 'items-start justify-start'
            : 'items-center justify-center',
        )}
        style={style}
        onPointerEnter={() => setHoveredElementId(id)}
        onPointerLeave={() => setHoveredElementId(null)}
        onFocus={handleFocus}
        onClick={handleClick}
        onDoubleClick={handleDoubleClick}
      >
        {/* A. 装饰层 (仅外部钩子存在时渲染壳) */}
        <ElementMetadataWrapper element={element} />

        {/* B. 内容渲染器 */}
        {content}
      </div>
    )
  },
)

/**
 * 元数据定位壳，完全由业务层提供内容
 */
function ElementMetadataWrapper({ element }: { element: SceneElement }) {
  const { renderElementMetadata } = useDominoRenderHooks()

  // 1. 昂贵的碰撞检测逻辑依然保留在内核，用于自动隐藏可能被遮挡的元信息
  const isMetadataHidden = useDominoStore(
    state => {
      // 如果业务层未定义钩子，虽然这个 Hook 会运行，但我们让他返回一个简单值
      if (!renderElementMetadata) return true

      // 画板元信息通常总是显示，但业务层也可以自行控制其内容
      if (element.type === 'artboard') return false
      // 目前主要为图片和画板支持元信息展示
      if (element.type !== 'image') return true

      const el = state.elements[element.id] || state.placeholders[element.id]
      if (!el) return true

      // 检测是否被其他元素遮盖了元信息显示区域
      const metaHeight = 22
      const metaYMin = el.y - metaHeight
      const metaYMax = el.y
      const metaXMin = el.x
      const metaXMax = el.x + el.width

      const allElements = [
        ...Object.values(state.elements),
        ...Object.values(state.placeholders),
      ]

      return allElements.some(other => {
        if (other.id === el.id || other.id === el.parentId) return false
        const otherXMax = other.x + (other.width || 0)
        const otherYMax = other.y + (other.height || 0)
        return (
          !(otherXMax < metaXMin || other.x > metaXMax) &&
          !(otherYMax < metaYMin || other.y > metaYMax)
        )
      })
    },
    (a, b) => a === b,
  )

  // 2. 只有定义了钩子且未被隐藏时才渲染
  if (!renderElementMetadata || isMetadataHidden || element.hideMetadata)
    return null

  const renderedContent = renderElementMetadata({ element })
  if (!renderedContent) return null

  return (
    <div
      className='domino-element-metadata absolute left-0 w-full z-50 pointer-events-none'
      style={{
        bottom: '100%',
        width: 'calc(var(--domino-scale) * 100%)',
        transform: 'scale(calc(1 / var(--domino-scale)))',
        transformOrigin: '0 100%',
        paddingBottom: 4,
        fontSize: 11,
      }}
    >
      {renderedContent}
    </div>
  )
}
