// Horizontal drag-to-scroll rail with a grab cursor.
// Children render in a flex row; each child sets its own fixed width
// (e.g. flex: '0 0 360px').
//
// Design notes / past bugs fixed:
// - NO pointer-capture (it swallowed clicks on child cards → cards weren't
//   clickable). We listen on window for mousemove/mouseup instead.
// - A real click (no movement) passes through to the card. A drag (>5px)
//   is swallowed by onClickCapture so the card doesn't navigate after a slide.
// - grab/grabbing cursor is forced with !important so child cards' inline
//   cursor:pointer doesn't hide the "this is a slider" affordance.
// - scroll-snap is ON at rest (x mandatory + smooth) so the rail always
//   settles flush to a card edge — never a half-cut card. During an active
//   drag the .is-grabbing class turns snap OFF (so the 1:1 drag stays smooth,
//   no jerky mid-drag snapping); on release snap re-engages and the browser
//   glides to the nearest card.
function DragScrollRail({ children, gap = 20, padBottom = 16, fade = true, style }) {
  const ref = React.useRef(null);
  const st = React.useRef({ down: false, startX: 0, startLeft: 0, moved: false });
  const [hasOverflow, setHasOverflow] = React.useState(false);

  // Track overflow to show the right-edge fade hint.
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const measure = () => setHasOverflow(el.scrollWidth - el.clientWidth > 4);
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, []);

  // Drag handlers live on window so the drag keeps working when the cursor
  // leaves the rail.
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const onMove = (e) => {
      if (!st.current.down) return;
      const dx = e.clientX - st.current.startX;
      if (!st.current.moved && Math.abs(dx) > 5) {
        st.current.moved = true;
        el.classList.add('is-dragging');
      }
      if (st.current.moved) {
        el.scrollLeft = st.current.startLeft - dx;
        e.preventDefault();
      }
    };
    const onUp = () => {
      if (!st.current.down) return;
      st.current.down = false;
      el.classList.remove('is-grabbing');
      // Keep is-dragging through the synchronous click that follows mouseup
      // (so the click is swallowed on the rail), then drop it next tick.
      if (st.current.moved) {
        setTimeout(() => el.classList.remove('is-dragging'), 0);
      } else {
        el.classList.remove('is-dragging');
      }
    };
    window.addEventListener('mousemove', onMove, { passive: false });
    window.addEventListener('mouseup', onUp);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', onUp);
    };
  }, []);

  const onMouseDown = (e) => {
    if (e.button !== 0) return; // primary only
    const el = ref.current;
    if (!el) return;
    st.current = { down: true, startX: e.clientX, startLeft: el.scrollLeft, moved: false };
    el.classList.add('is-grabbing');
  };
  // If a drag happened, swallow the click so child cards don't navigate.
  const onClickCapture = (e) => {
    if (st.current.moved) {
      e.preventDefault();
      e.stopPropagation();
      st.current.moved = false;
    }
  };

  return (
    <div style={{ position: 'relative', ...style }}>
      <div
        ref={ref}
        className="drag-rail"
        onMouseDown={onMouseDown}
        onClickCapture={onClickCapture}
        style={{
          display: 'flex', gap,
          overflowX: 'auto', overflowY: 'hidden',
          paddingBottom: padBottom, marginInline: -8, paddingInline: 8,
          WebkitOverflowScrolling: 'touch',
        }}>
        {children}
      </div>
      {/* Right-edge fade — visual hint that there's more */}
      {fade && hasOverflow && (
        <div style={{
          position: 'absolute', right: 0, top: 0, bottom: padBottom,
          width: 64, pointerEvents: 'none',
          background: 'linear-gradient(90deg, transparent, var(--bg) 80%)',
        }} />
      )}
      <style dangerouslySetInnerHTML={{ __html: `
        /* Snap to card edges at rest; smooth glide on release. */
        .drag-rail { scroll-snap-type: x mandatory; scroll-behavior: smooth; }
        .drag-rail > * { scroll-snap-align: start; }
        /* While actively dragging: no snap, instant 1:1 follow. */
        .drag-rail.is-grabbing { scroll-snap-type: none; scroll-behavior: auto; }
        .drag-rail, .drag-rail * { cursor: grab !important; }
        .drag-rail.is-grabbing, .drag-rail.is-grabbing * { cursor: grabbing !important; }
        .drag-rail.is-dragging * { pointer-events: none; }
        .drag-rail::-webkit-scrollbar { height: 6px; }
        .drag-rail::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.18); border-radius: 3px; }
        .drag-rail::-webkit-scrollbar-track { background: transparent; }
      `}} />
    </div>
  );
}

window.DragScrollRail = DragScrollRail;
