import { useCallback, useEffect, useId, useRef, useState, type CSSProperties } from 'react'
import { createPortal } from 'react-dom'
import {
  domainAvatarHue,
  domainAvatarLabel,
  sourceDisplayTitle,
  sourceDomain,
  type SourcePreviewItem,
} from '../lib/sourcePreview'

type SourceLinkPreviewProps = {
  item: SourcePreviewItem
  compact?: boolean
  className?: string
}

type PreviewPosition = {
  top: number
  left: number
  placement: 'above' | 'below'
}

const PREVIEW_WIDTH = 288
const PREVIEW_GAP = 10
const SHOW_DELAY_MS = 140
const HIDE_DELAY_MS = 120

function clamp(value: number, min: number, max: number) {
  return Math.min(Math.max(value, min), max)
}

export function SourceLinkPreview({ item, compact = false, className = '' }: SourceLinkPreviewProps) {
  const url = item.url?.trim()
  const title = sourceDisplayTitle(item)
  const domain = url ? sourceDomain(url) : ''
  const snippet = item.snippet?.trim()
  const query = item.query?.trim()
  const previewId = useId()

  const anchorRef = useRef<HTMLAnchorElement>(null)
  const cardRef = useRef<HTMLDivElement>(null)
  const showTimer = useRef<number | undefined>(undefined)
  const hideTimer = useRef<number | undefined>(undefined)

  const [open, setOpen] = useState(false)
  const [position, setPosition] = useState<PreviewPosition | null>(null)

  const hasPreview = !!(snippet || query || (title && title !== domain))

  const updatePosition = useCallback(() => {
    const anchor = anchorRef.current
    if (!anchor) return
    const rect = anchor.getBoundingClientRect()
    const cardHeight = cardRef.current?.offsetHeight ?? 168
    const viewportPadding = 12
    const spaceBelow = window.innerHeight - rect.bottom
    const placement: PreviewPosition['placement'] =
      spaceBelow < cardHeight + PREVIEW_GAP + 24 && rect.top > cardHeight + PREVIEW_GAP
        ? 'above'
        : 'below'
    const top =
      placement === 'below'
        ? rect.bottom + PREVIEW_GAP
        : rect.top - cardHeight - PREVIEW_GAP
    const left = clamp(
      rect.left + rect.width / 2 - PREVIEW_WIDTH / 2,
      viewportPadding,
      window.innerWidth - PREVIEW_WIDTH - viewportPadding,
    )
    setPosition({ top, left, placement })
  }, [])

  const showPreview = useCallback(() => {
    if (!hasPreview || !url) return
    window.clearTimeout(hideTimer.current)
    updatePosition()
    setOpen(true)
  }, [hasPreview, updatePosition, url])

  const scheduleShow = useCallback(() => {
    window.clearTimeout(hideTimer.current)
    window.clearTimeout(showTimer.current)
    showTimer.current = window.setTimeout(showPreview, SHOW_DELAY_MS)
  }, [showPreview])

  const scheduleHide = useCallback(() => {
    window.clearTimeout(showTimer.current)
    window.clearTimeout(hideTimer.current)
    hideTimer.current = window.setTimeout(() => setOpen(false), HIDE_DELAY_MS)
  }, [])

  useEffect(() => {
    if (!open) return
    updatePosition()
    const onScroll = () => updatePosition()
    window.addEventListener('scroll', onScroll, true)
    window.addEventListener('resize', onScroll)
    return () => {
      window.removeEventListener('scroll', onScroll, true)
      window.removeEventListener('resize', onScroll)
    }
  }, [open, updatePosition])

  useEffect(() => {
    return () => {
      window.clearTimeout(showTimer.current)
      window.clearTimeout(hideTimer.current)
    }
  }, [])

  if (!url) {
    return <span className={`hx-source-chip hx-source-chip--static ${className}`}>{title}</span>
  }

  const hue = domainAvatarHue(domain)

  return (
    <>
      <a
        ref={anchorRef}
        href={url}
        target="_blank"
        rel="noreferrer"
        className={`hx-source-chip ${compact ? 'hx-source-chip--compact' : ''} ${className}`}
        aria-describedby={open && hasPreview ? previewId : undefined}
        onMouseEnter={scheduleShow}
        onMouseLeave={scheduleHide}
        onFocus={showPreview}
        onBlur={scheduleHide}
      >
        <span
          className="hx-source-chip__avatar"
          style={{ '--hx-source-hue': `${hue}` } as CSSProperties}
          aria-hidden
        >
          {domainAvatarLabel(domain)}
        </span>
        <span className="hx-source-chip__text">
          <span className="hx-source-chip__domain hx-read-url">{domain}</span>
          {!compact ? <span className="hx-source-chip__title">{title}</span> : null}
        </span>
      </a>

      {open && hasPreview && position
        ? createPortal(
            <div
              ref={cardRef}
              id={previewId}
              role="tooltip"
              className={`hx-source-preview hx-source-preview--${position.placement}`}
              style={{ top: position.top, left: position.left, width: PREVIEW_WIDTH }}
              onMouseEnter={scheduleShow}
              onMouseLeave={scheduleHide}
            >
              <div className="hx-source-preview__hero">
                <span
                  className="hx-source-preview__avatar"
                  style={{ '--hx-source-hue': `${hue}` } as CSSProperties}
                  aria-hidden
                >
                  {domainAvatarLabel(domain)}
                </span>
                <div className="hx-source-preview__hero-text">
                  <span className="hx-source-preview__domain hx-read-url">{domain}</span>
                  {query ? <span className="hx-source-preview__query">搜尋：{query}</span> : null}
                </div>
              </div>
              <p className="hx-source-preview__title">{title}</p>
              {snippet ? <p className="hx-source-preview__snippet">{snippet}</p> : null}
              <p className="hx-source-preview__hint">移開滑鼠後可點連結開啟</p>
            </div>,
            document.body,
          )
        : null}
    </>
  )
}