'use client'

import { useEffect, useState, useRef, RefObject, useCallback } from 'react'

interface UseStickyOptions {
  /**
   * 粘性触发的偏移量（相对于容器顶部）
   * 默认为 0
   */
  offset?: number
  /**
   * 抖动防护的滞后容差（像素）。
   * 会将触发边界向下偏移，避免 0 边界来回切换。
   * 默认为 2
   */
  hysteresis?: number
  /**
   * 根元素，用于观察交叉状态
   * 默认为 null（使用视口）
   */
  root?: Element | null
}

/**
 * 检测元素是否处于 sticky 状态的 hook
 *
 * @param options 配置选项
 * @returns [ref, isSticky] - 需要绑定到目标元素的 ref 和 sticky 状态
 */
export function useSticky<T extends HTMLElement = HTMLDivElement>(
  options: UseStickyOptions = {}
): [RefObject<T | null>, boolean] {
  const { offset = 0, hysteresis = 2, root = null } = options
  const elementRef = useRef<T>(null)
  const [isSticky, setIsSticky] = useState(false)
  const rafIdRef = useRef<number | null>(null)
  const lastCheckTimeRef = useRef<number>(0)
  const scrollContainerRef = useRef<HTMLElement | null>(null)

  // 检查并更新 sticky 状态
  const checkStickyState = useCallback(() => {
    const element = elementRef.current
    const scrollContainer = scrollContainerRef.current

    if (!element) return

    // 获取滚动容器的滚动位置
    const scrollTop = scrollContainer ? scrollContainer.scrollTop : window.scrollY

    // 获取元素相对于容器的位置
    const elementRect = element.getBoundingClientRect()
    const containerRect = scrollContainer ? scrollContainer.getBoundingClientRect() : { top: 0 }

    // 计算元素相对于容器顶部的距离
    const elementTopRelativeToContainer = elementRect.top - containerRect.top

    // 滚动位置阈值：避免在接近顶部时还保持吸顶
    const scrollThreshold = 20

    // 判断是否应该吸顶
    // 向下滚动时：元素顶部到达或超过 offset 位置时触发吸顶
    // 向上滚动时：元素顶部回到 offset + hysteresis 以上时取消吸顶
    const shouldBeSticky = elementTopRelativeToContainer <= offset && scrollTop > scrollThreshold
    const shouldNotBeSticky = elementTopRelativeToContainer > offset + hysteresis

    setIsSticky((prev) => {
      if (shouldNotBeSticky) return false
      if (shouldBeSticky) return true
      return prev // 保持当前状态
    })
  }, [offset, hysteresis])

  useEffect(() => {
    const element = elementRef.current
    if (!element) return

    // 如果没有指定 root，尝试自动查找滚动容器
    let scrollContainer = root as HTMLElement | null
    if (!scrollContainer) {
      // 查找具有 overflow 属性的父容器或 main-content 容器
      scrollContainer = document.getElementById('main-content') || null
    }
    scrollContainerRef.current = scrollContainer

    // 创建一个哨兵元素来检测 sticky 状态
    const sentinel = document.createElement('div')
    sentinel.style.position = 'relative'
    sentinel.style.top = `${offset}px` // 设置与 sticky offset 相同的位置
    sentinel.style.left = '0'
    sentinel.style.width = '1px'
    sentinel.style.height = '1px'
    sentinel.style.pointerEvents = 'none'
    sentinel.style.visibility = 'hidden'

    // 将哨兵元素插入到目标元素之前
    element.parentNode?.insertBefore(sentinel, element)

    // IntersectionObserver 作为主要检测机制
    const observer = new IntersectionObserver(
      ([entry]) => {
        // 使用 rAF 合并更新，避免高频切换
        if (rafIdRef.current !== null) {
          cancelAnimationFrame(rafIdRef.current)
        }
        rafIdRef.current = requestAnimationFrame(() => {
          checkStickyState()
        })
      },
      {
        root: scrollContainer,
        // 在向下滚动触发点加入滞后容差，远离 0 边界，减少抖动
        rootMargin: `-${offset + Math.max(0, Math.floor(hysteresis))}px 0px 0px 0px`,
        threshold: 0,
      }
    )

    observer.observe(sentinel)

    // 添加 scroll 事件监听作为后备方案
    // 使用节流来优化性能，避免过于频繁的调用
    let scrollTimeout: NodeJS.Timeout | null = null
    const handleScroll = () => {
      const now = Date.now()
      // 节流：至少间隔 50ms 才执行一次检查
      if (now - lastCheckTimeRef.current < 50) {
        if (scrollTimeout) {
          clearTimeout(scrollTimeout)
        }
        // 设置一个延迟检查，确保滚动停止后也会执行一次
        scrollTimeout = setTimeout(() => {
          lastCheckTimeRef.current = Date.now()
          checkStickyState()
        }, 50)
        return
      }

      lastCheckTimeRef.current = now
      checkStickyState()
    }

    const scrollTarget = scrollContainer || window
    scrollTarget.addEventListener('scroll', handleScroll, { passive: true })

    return () => {
      observer.disconnect()
      sentinel.remove()
      scrollTarget.removeEventListener('scroll', handleScroll)
      if (rafIdRef.current !== null) {
        cancelAnimationFrame(rafIdRef.current)
        rafIdRef.current = null
      }
      if (scrollTimeout) {
        clearTimeout(scrollTimeout)
      }
    }
  }, [offset, hysteresis, root, checkStickyState])

  return [elementRef, isSticky]
}

/**
 * 简化版本的 useSticky hook，使用默认配置
 */
export function useStickySimple<T extends HTMLElement = HTMLDivElement>(): [
  RefObject<T | null>,
  boolean,
] {
  return useSticky<T>()
}
