import { forwardRef, useLayoutEffect, useRef, useState } from 'react'
import type { Suggestion } from '../../api/client'
import { AskPetal } from './AskPetal'
import { TYPE_META } from './suggestionMeta'

// Vertical breathing room kept between stacked cards when their natural anchors
// would otherwise collide.
const CARD_GAP = 12

export interface RailItem {
  suggestion: Suggestion
  // Vertical offset (px) of the suggestion's highlight, relative to the editor
  // wrapper — the position the card wants to sit beside.
  anchorTop: number
}

interface Props {
  items: RailItem[]
  // The suggestion currently emphasized (its highlight hovered, or this card
  // hovered) — gets a lifted, ring-accented treatment so the text↔card link reads.
  activeId: string | null
  // The card expanded to show the full explanation + Ask Petal, or null.
  expandedId: string | null
  onAccept: (s: Suggestion) => void
  onDismiss: (s: Suggestion) => void
  // Pointer entering/leaving a card, so the matching highlight can light up.
  onHover: (id: string | null) => void
  // A card's body was clicked — scroll its highlight into view and toggle expand.
  onActivate: (id: string) => void
  onToggleExpand: (id: string) => void
}

// SuggestionRail is the right-margin "comment column": every outstanding
// suggestion as a card, vertically aligned to the text it flags, so the writer
// sees the whole queue at once instead of hunting highlight by highlight. Cards
// are anchored to their highlight's vertical position and pushed down only as far
// as needed to avoid overlapping their neighbor above (a la doc-editor comments).
export function SuggestionRail({
  items,
  activeId,
  expandedId,
  onAccept,
  onDismiss,
  onHover,
  onActivate,
  onToggleExpand,
}: Props) {
  // Measured resolved tops keyed by suggestion id (after collision avoidance).
  const [tops, setTops] = useState<Record<string, number>>({})
  const cardRefs = useRef<Map<string, HTMLDivElement>>(new Map())
  // Bumped whenever a card's own height changes (Ask Petal streaming in, wrapping
  // text) so the stack re-packs and cards below don't get overlapped.
  const [measureTick, setMeasureTick] = useState(0)
  const observerRef = useRef<ResizeObserver | null>(null)
  if (!observerRef.current && typeof ResizeObserver !== 'undefined') {
    observerRef.current = new ResizeObserver(() => setMeasureTick((t) => t + 1))
  }
  useLayoutEffect(() => () => observerRef.current?.disconnect(), [])

  // Sort by anchor, then greedily stack: each card sits at its anchor unless that
  // would overlap the previous card, in which case it slides down just enough.
  // Re-runs whenever the anchors or the expanded card (which changes a height)
  // shift. Reads live offsetHeight so variable-length explanations pack tightly.
  const ordered = [...items].sort((a, b) => a.anchorTop - b.anchorTop)
  const layoutKey = ordered.map((i) => `${i.suggestion.id}:${Math.round(i.anchorTop)}`).join('|')
  useLayoutEffect(() => {
    let cursor = -Infinity
    const next: Record<string, number> = {}
    for (const { suggestion, anchorTop } of ordered) {
      const h = cardRefs.current.get(suggestion.id)?.offsetHeight ?? 96
      const top = Math.max(anchorTop, cursor)
      next[suggestion.id] = top
      cursor = top + h + CARD_GAP
    }
    setTops((prev) => {
      const ids = Object.keys(next)
      if (ids.length === Object.keys(prev).length && ids.every((id) => prev[id] === next[id])) return prev
      return next
    })
    // layoutKey captures anchor positions; expandedId/measureTick capture height
    // changes (toggling detail, Ask Petal streaming in).
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [layoutKey, expandedId, measureTick])

  return (
    <div className="petal-rail petal-no-print" aria-label="Suggestions">
      {ordered.map(({ suggestion }) => (
        <RailCard
          key={suggestion.id}
          ref={(el) => {
            const prev = cardRefs.current.get(suggestion.id)
            if (prev && prev !== el) observerRef.current?.unobserve(prev)
            if (el) {
              cardRefs.current.set(suggestion.id, el)
              observerRef.current?.observe(el)
            } else {
              cardRefs.current.delete(suggestion.id)
            }
          }}
          suggestion={suggestion}
          top={tops[suggestion.id] ?? 0}
          active={activeId === suggestion.id}
          expanded={expandedId === suggestion.id}
          onAccept={onAccept}
          onDismiss={onDismiss}
          onHover={onHover}
          onActivate={onActivate}
          onToggleExpand={onToggleExpand}
        />
      ))}
    </div>
  )
}

interface CardProps {
  suggestion: Suggestion
  top: number
  active: boolean
  expanded: boolean
  onAccept: (s: Suggestion) => void
  onDismiss: (s: Suggestion) => void
  onHover: (id: string | null) => void
  onActivate: (id: string) => void
  onToggleExpand: (id: string) => void
}

const RailCard = forwardRef<HTMLDivElement, CardProps>(function RailCard(
  { suggestion, top, active, expanded, onAccept, onDismiss, onHover, onActivate, onToggleExpand },
  ref,
) {
  const meta = TYPE_META[suggestion.type]
  const hasReplacement = suggestion.replacement.trim() !== ''

  return (
    <div
      ref={ref}
      role="group"
      aria-label={`${meta.label} suggestion`}
      onMouseEnter={() => onHover(suggestion.id)}
      onMouseLeave={() => onHover(null)}
      className={`petal-rail-card${active ? ' petal-rail-card-active' : ''}`}
      style={{ top, borderLeftColor: meta.color }}
    >
      <div className="flex items-center justify-between">
        <span
          className="inline-flex items-center rounded-full px-2 py-0.5 text-[0.68rem] font-bold"
          style={{ background: meta.color, color: 'var(--color-plum)' }}
        >
          {meta.label}
        </span>
        <button
          type="button"
          aria-label="Dismiss suggestion"
          onClick={() => onDismiss(suggestion)}
          className="petal-rail-x"
        >
          ✕
        </button>
      </div>

      {/* The body toggles the expanded explanation + Ask Petal and points the
          editor at the flagged text. */}
      <button type="button" onClick={() => onActivate(suggestion.id)} className="mt-2 block w-full text-left">
        {hasReplacement && (
          <span className="flex flex-col gap-0.5" style={{ fontFamily: 'var(--font-body)' }}>
            <span className="truncate text-[0.9rem] line-through" style={{ color: 'var(--color-muted)' }}>
              {suggestion.original}
            </span>
            <span className="truncate text-[0.9rem] font-medium" style={{ color: 'var(--color-plum)' }}>
              {suggestion.replacement}
            </span>
          </span>
        )}
        <span
          className={`mt-1.5 block text-[0.82rem] leading-snug${expanded ? '' : ' line-clamp-2'}`}
          style={{ color: 'var(--color-plum)' }}
        >
          {suggestion.explanation}
        </span>
      </button>

      {expanded && <AskPetal suggestionId={suggestion.id} explanation={suggestion.explanation} />}

      <div className="mt-2.5 flex items-center gap-2">
        {hasReplacement && (
          <button
            type="button"
            onClick={() => onAccept(suggestion)}
            className="rounded-full px-3 py-1 text-xs font-bold text-white"
            style={{ background: 'var(--color-accent)' }}
            onMouseEnter={(e) => (e.currentTarget.style.background = 'var(--color-accent-hover)')}
            onMouseLeave={(e) => (e.currentTarget.style.background = 'var(--color-accent)')}
          >
            Accept
          </button>
        )}
        <button
          type="button"
          onClick={() => onToggleExpand(suggestion.id)}
          className="rounded-full px-2.5 py-1 text-xs font-bold transition-colors"
          style={{
            background: expanded ? 'var(--color-surface-alt)' : 'transparent',
            color: 'var(--color-accent-hover)',
          }}
        >
          {expanded ? 'Hide Petal' : 'Ask Petal ✨'}
        </button>
      </div>
    </div>
  )
})
