import React, { useMemo, useState, useCallback, useRef, useEffect } from 'react'
import { useDominoAnchor, type AnchorRect } from './use-domino-anchor'

export type Placement =
  | 'top'
  | 'top-start'
  | 'top-end'
  | 'bottom'
  | 'bottom-start'
  | 'bottom-end'
  | 'left'
  | 'left-start'
  | 'left-end'
  | 'right'
  | 'right-start'
  | 'right-end'

export interface DominoAnchorProps {
  /** The ID of the element on the canvas to anchor to */
  id: string | null
  /** The container ref where the canvas is rendered */
  containerRef: React.RefObject<HTMLElement | null>
  /** Alignment preference */
  placement?: Placement
  /** Margin from the anchor element */
  offset?: number
  /** Whether to use absolute screen space coordinates (for body portals) */
  screenSpace?: boolean

  // --- Props for Simple/Advanced Usage ---
  className?: string
  style?: React.CSSProperties
  /**
   * Children can be either a ReactNode (simple usage) or a render function (advanced control).
   * If a function is provided, it receives calculated style, anchor, and measureRef.
   */
  children?:
    | React.ReactNode
    | ((props: {
        style: React.CSSProperties
        anchor: AnchorRect
        measureRef: (node: HTMLElement | null) => void
      }) => React.ReactNode)
}

export const DominoAnchor: React.FC<DominoAnchorProps> = ({
  id,
  containerRef,
  placement = 'right-start',
  offset = 12,
  screenSpace = false,
  className,
  style,
  children,
}) => {
  const anchor = useDominoAnchor(id, containerRef, screenSpace)
  const [size, setSize] = useState({ width: 0, height: 0 })
  const resizeObserverRef = useRef<ResizeObserver | null>(null)

  const measureRef = useCallback((node: HTMLElement | null) => {
    if (resizeObserverRef.current) {
      resizeObserverRef.current.disconnect()
    }

    if (node) {
      // Immediate measurement
      const rect = node.getBoundingClientRect()
      setSize(prev => {
        if (prev.width === rect.width && prev.height === rect.height)
          return prev
        return { width: rect.width, height: rect.height }
      })

      // Setup observer for dynamic changes
      if (!resizeObserverRef.current) {
        resizeObserverRef.current = new ResizeObserver(entries => {
          const entry = entries[0]
          if (entry) {
            const { width, height } = entry.contentRect
            setSize(prev => {
              if (prev.width === width && prev.height === height) return prev
              return { width, height }
            })
          }
        })
      }
      resizeObserverRef.current.observe(node)
    }
  }, [])

  useEffect(() => {
    return () => {
      if (resizeObserverRef.current) {
        resizeObserverRef.current.disconnect()
      }
    }
  }, [])

  const finalStyle = useMemo((): React.CSSProperties | null => {
    if (!anchor) return null

    let left = 0
    let top = 0
    const { width: floatingWidth, height: floatingHeight } = size

    // Initial placement logic (Standard 12 positions)
    switch (placement) {
      // TOP
      case 'top':
        left = anchor.centerX - floatingWidth / 2
        top = anchor.top - floatingHeight - offset
        break
      case 'top-start':
        left = anchor.left
        top = anchor.top - floatingHeight - offset
        break
      case 'top-end':
        left = anchor.right - floatingWidth
        top = anchor.top - floatingHeight - offset
        break

      // BOTTOM
      case 'bottom':
        left = anchor.centerX - floatingWidth / 2
        top = anchor.bottom + offset
        break
      case 'bottom-start':
        left = anchor.left
        top = anchor.bottom + offset
        break
      case 'bottom-end':
        left = anchor.right - floatingWidth
        top = anchor.bottom + offset
        break

      // LEFT
      case 'left':
        left = anchor.left - floatingWidth - offset
        top = anchor.centerY - floatingHeight / 2
        break
      case 'left-start':
        left = anchor.left - floatingWidth - offset
        top = anchor.top
        break
      case 'left-end':
        left = anchor.left - floatingWidth - offset
        top = anchor.bottom - floatingHeight
        break

      // RIGHT
      case 'right':
        left = anchor.right + offset
        top = anchor.centerY - floatingHeight / 2
        break
      case 'right-start':
        left = anchor.right + offset
        top = anchor.top
        break
      case 'right-end':
        left = anchor.right + offset
        top = anchor.bottom - floatingHeight
        break

      default:
        left = anchor.right + offset
        top = anchor.top
    }

    // --- Automatic Boundary Avoidance (Collision Detection) ---
    const viewportWidth =
      screenSpace || !containerRef.current
        ? window.innerWidth
        : containerRef.current.clientWidth
    const viewportHeight =
      screenSpace || !containerRef.current
        ? window.innerHeight
        : containerRef.current.clientHeight

    const containerRect = containerRef.current?.getBoundingClientRect()
    // Define screen-space or relative boundaries
    const minX = screenSpace && containerRect ? containerRect.left : 0
    const maxX =
      screenSpace && containerRect ? containerRect.right : viewportWidth
    const minY = screenSpace && containerRect ? containerRect.top : 0
    const maxY =
      screenSpace && containerRect ? containerRect.bottom : viewportHeight

    // 3. Clamping & Sticking Logic (Horizontal)
    // We treat the "near" edge of the popup (the one relative to the anchor) as the sticky point
    if (placement.includes('left')) {
      // "当弹窗在元素左侧出现时，弹窗的 right side edge 与窗口的 left side edge 相吸附"
      const currentRight = left + floatingWidth
      const clampedRight = Math.max(minX, Math.min(currentRight, maxX))
      left = clampedRight - floatingWidth
    } else if (placement.includes('right')) {
      left = Math.max(minX, Math.min(left, maxX))
    }

    // Vertical stay-within-bounds (Keep it in view vertically)
    if (top + floatingHeight > maxY) {
      top = maxY - floatingHeight
    }
    if (top < minY) {
      top = minY
    }

    return {
      position: screenSpace ? 'fixed' : ('absolute' as const),
      left,
      top,
      zIndex: 1000,
      pointerEvents: 'auto' as const,
      visibility: (size.width === 0
        ? 'hidden'
        : 'visible') as React.CSSProperties['visibility'],
    }
  }, [anchor, placement, offset, size, containerRef, screenSpace])

  if (!anchor || !finalStyle) return null

  // Advanced Usage: children is a render function
  if (typeof children === 'function') {
    return <>{children({ style: finalStyle, anchor, measureRef })}</>
  }

  // Simple Usage: wrap children in a div with position styles
  return (
    <div
      ref={measureRef}
      style={{ ...finalStyle, ...style }}
      className={className}
    >
      {children}
    </div>
  )
}
