import { useState, useRef, useLayoutEffect, type KeyboardEvent, type ReactNode } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { cn } from '@/lib/utils'
import { MOTION_EXPAND } from '@/lib/motion'
import { ArrowDownSLine } from './icons-inline'

export interface CollapsibleCardProps {
  headerIcon?: ReactNode
  headerTitle: ReactNode
  headerRight?: ReactNode
  children?: ReactNode
  defaultExpanded?: boolean
  /** CSS padding 如 '6px 8px'，或 Tailwind 类 */
  contentPadding?: string
  onToggle?: (expanded: boolean) => void
  collapsible?: boolean
  footer?: ReactNode
  contentMaxHeight?: number
  /** 是否启用内容区底部展开/收起栏：内容高度超过 maxHeight 时显示，点击切换全部展开或收起 */
  showExpandAllBar?: boolean
  /** showExpandAllBar 为 true 时，未展开全部时的内容区最大高度（px） */
  maxHeight?: number
  titleShimmer?: boolean
  className?: string
}

const DEFAULT_MAX_HEIGHT = 124

export function CollapsibleCard({
  headerIcon,
  headerTitle,
  headerRight,
  children,
  defaultExpanded = true,
  contentPadding = 'var(--spacing-1.5) var(--spacing-2)',
  onToggle,
  collapsible = true,
  footer,
  contentMaxHeight,
  showExpandAllBar = false,
  maxHeight = DEFAULT_MAX_HEIGHT,
  titleShimmer = false,
  className,
}: CollapsibleCardProps) {
  const [isExpanded, setIsExpanded] = useState(defaultExpanded)
  const [isHovered, setIsHovered] = useState(false)
  const [expandAll, setExpandAll] = useState(false)
  const [shouldShowBar, setShouldShowBar] = useState(false)
  const [contentHeight, setContentHeight] = useState(0)
  const [isMeasuring, setIsMeasuring] = useState(showExpandAllBar && defaultExpanded)
  const [isAnimating, setIsAnimating] = useState(false)
  const measureRef = useRef<HTMLDivElement>(null)

  useLayoutEffect(() => {
    if (!showExpandAllBar || !isExpanded) {
      setShouldShowBar(false)
      setContentHeight(0)
      setIsMeasuring(false)
      return
    }
    setIsMeasuring(true)
    const checkHeight = () => {
      const el = measureRef.current
      if (!el) return
      const actualHeight = el.scrollHeight
      setContentHeight(actualHeight)
      setShouldShowBar(actualHeight > maxHeight)
      setIsMeasuring(false)
    }
    const raf = requestAnimationFrame(checkHeight)
    let resizeObserver: ResizeObserver | null = null
    if (measureRef.current) {
      resizeObserver = new ResizeObserver(checkHeight)
      resizeObserver.observe(measureRef.current)
    }
    return () => {
      cancelAnimationFrame(raf)
      resizeObserver?.disconnect()
    }
  }, [showExpandAllBar, isExpanded, children, maxHeight])

  const handleToggle = () => {
    if (!collapsible) return
    const next = !isExpanded
    setIsAnimating(true)
    if (next && showExpandAllBar) setIsMeasuring(true)
    setIsExpanded(next)
    onToggle?.(next)
    setTimeout(() => setIsAnimating(false), 300)
  }

  const handleToggleKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
    if (!collapsible) return
    if (event.key !== 'Enter' && event.key !== ' ') return
    event.preventDefault()
    handleToggle()
  }

  const showArrow = collapsible && (isHovered || headerIcon == null)
  const headerIconNode = showArrow
    ? <ArrowDownSLine className={cn('w-[var(--font-size-sm)] h-[var(--font-size-sm)] shrink-0 transition-transform duration-200 text-text-secondary', isExpanded ? 'rotate-0' : '-rotate-90')} />
    : headerIcon

  const useExpandAllLogic = showExpandAllBar && isExpanded && !contentMaxHeight
  const contentClamped = useExpandAllLogic && (isMeasuring || (shouldShowBar && !expandAll) || isAnimating || (!shouldShowBar && contentHeight === 0))
  const contentMaxHeightStyle =
    contentMaxHeight != null
      ? `${contentMaxHeight}px`
      : contentClamped
        ? `${maxHeight}px`
        : useExpandAllLogic && shouldShowBar && expandAll && contentHeight > 0
          ? `${Math.max(contentHeight + 50, 2000)}px`
          : undefined

  return (
    <div
      className={cn(
        'rounded-md overflow-hidden border border-border-tertiary bg-fill-tertiary relative',
        className
      )}
    >
      <div
        className={cn(
          'flex flex-row items-center gap-2 px-2 py-1.5 select-none',
          isExpanded && 'border-b border-border-tertiary'
        )}
        onMouseEnter={() => collapsible && setIsHovered(true)}
        onMouseLeave={() => collapsible && setIsHovered(false)}
      >
        <div
          className={cn('flex flex-row items-center gap-2 flex-1 min-w-0', collapsible && 'cursor-pointer')}
          onClick={handleToggle}
          onKeyDown={handleToggleKeyDown}
          role={collapsible ? 'button' : undefined}
          tabIndex={collapsible ? 0 : undefined}
          aria-expanded={collapsible ? isExpanded : undefined}
        >
          <div className="w-[var(--font-size-sm)] h-[var(--font-size-sm)] flex items-center justify-center shrink-0 text-text-secondary [&>svg]:w-[var(--font-size-sm)] [&>svg]:h-[var(--font-size-sm)]">
            {headerIconNode}
          </div>
          <div className="min-w-0 flex-1 flex items-center text-xs leading-xs text-text-secondary">
            {titleShimmer ? (
              <span className="animate-pulse">{headerTitle}</span>
            ) : (
              headerTitle
            )}
          </div>
        </div>
        {headerRight != null && (
          <div
            className="flex items-center gap-1.5 shrink-0 relative z-10"
            onClick={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
          >
            {headerRight}
          </div>
        )}
      </div>

      <AnimatePresence initial={false}>
        {isExpanded && (
          <motion.div
            {...MOTION_EXPAND}
            className={cn('overflow-hidden', isAnimating && 'select-none')}
          >
            {/* 隐藏测量用，用于获取内容实际高度 */}
            {useExpandAllLogic && (
              <div
                ref={measureRef}
                className="absolute invisible h-auto w-full pointer-events-none"
                style={{ padding: contentPadding }}
                aria-hidden
              >
                {children}
              </div>
            )}
          <div
            className={cn(
              'relative overflow-x-hidden bg-bg-base scrollbar-auto',
              contentMaxHeight != null
                ? 'overflow-y-auto'
                : contentClamped
                  ? 'overflow-y-hidden'
                  : 'overflow-y-visible',
              shouldShowBar && 'transition-[max-height] duration-300 ease-in-out'
            )}
            style={{
              padding: contentPadding,
              maxHeight: contentMaxHeightStyle,
            }}
          >
              {children}
              {/* 底部渐变遮罩：内容被截断且未展开全部时显示 */}
              {useExpandAllLogic && shouldShowBar && contentHeight > maxHeight && (
                <div
                  className={cn(
                    'absolute bottom-0 left-0 right-0 pointer-events-none h-6 bg-gradient-to-b from-transparent to-[var(--color-bg-base)] transition-opacity duration-300',
                    expandAll ? 'opacity-0' : 'opacity-100'
                  )}
                />
              )}
            </div>
            {/* 内容区底部展开/收起栏：仅当内容高度超过 maxHeight 时显示 */}
            {useExpandAllLogic && shouldShowBar && (
              <button
                type="button"
                className="w-full h-4 flex items-center justify-center bg-bg-base border-0 cursor-pointer text-text-secondary hover:text-text transition-colors"
                onClick={(e) => {
                  e.stopPropagation()
                  setIsAnimating(true)
                  setExpandAll((prev) => !prev)
                  setTimeout(() => setIsAnimating(false), 300)
                }}
                aria-label={expandAll ? '收起' : '展开全部'}
              >
                <ArrowDownSLine
                  className={cn(
                    'w-[var(--font-size-sm)] h-[var(--font-size-sm)] transition-transform duration-200',
                    expandAll ? 'rotate-180' : 'rotate-0'
                  )}
                />
              </button>
            )}
          </motion.div>
        )}
      </AnimatePresence>

      {footer != null && (
        <div className="flex flex-row items-center gap-2 px-2 py-1.5 border-t border-border-tertiary">
          {footer}
        </div>
      )}
    </div>
  )
}

CollapsibleCard.displayName = 'CollapsibleCard'
