// MotionMedia — shared "still that plays on hover" media fill, mirroring the
// behavior of motion.spencer-russell.com. Used by the project card (SP_Card),
// the detail image strip (CP_ImageTile), and the lightbox. Loads AFTER React /
// responsive.jsx and BEFORE category-page.jsx, subcategory-page.jsx, lightbox.jsx.
//
// Media object shape (an entry in a project's `images` array):
//   { tag, title }                              → striped placeholder + [tag]
//   { tag, title, src }                         → a static still (photo)
//   { tag, title, motion: true }                → placeholder marked as a MOTION
//                                                 slot (shows the ▶ badge) — use
//                                                 while you still owe a clip
//   { tag, title, src, video }                  → poster still that plays the
//                                                 clip on hover (the real thing)
//
//   src   — poster / still frame (jpg/png/webp). Shown when idle.
//   video — the clip (mp4/webm, short, muted-loop-friendly). Plays on hover.
//
// `active` (bool) drives playback — the parent passes its own hover state, so
// the same component animates from a card hover, a tile hover, or autoplay in
// the lightbox. Everything is muted + looped + playsInline so it autoplays.

function mm_useReducedMotion() {
  return typeof window.matchMedia === 'function'
    && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

// Does this media entry carry a playable clip?
function mediaHasVideo(m) { return !!(m && m.video); }
// Is this media entry a motion slot at all (clip present OR explicitly flagged)?
function mediaIsMotion(m) { return !!(m && (m.video || m.motion)); }

// Absolute-fill media layer. The PARENT must be `position: relative` and clip
// overflow; MotionMedia paints into it (poster/placeholder base + hover video +
// a small ▶ motion badge).
function MotionMedia({ media, active, showTag = true, badge = true }) {
  const m = media || {};
  const vref = React.useRef(null);
  const reduce = mm_useReducedMotion();
  const hasVideo = !!m.video;
  const motion = !!(m.video || m.motion);
  const poster = m.src;

  // Play while active (hovered / lightbox-open), pause + rewind when idle so the
  // card always returns to its poster frame.
  React.useEffect(() => {
    const v = vref.current;
    if (!v) return;
    if (active && !reduce) {
      const p = v.play();
      if (p && p.catch) p.catch(() => {});
    } else {
      try { v.pause(); } catch (e) {}
      if (!active) { try { v.currentTime = 0; } catch (e) {} }
    }
  }, [active, reduce]);

  return (
    <React.Fragment>
      {/* Base: poster still, or the striped placeholder when no still yet. */}
      <div style={{ position: 'absolute', inset: 0, backgroundColor: 'var(--paper)',
        backgroundImage: poster
          ? `url(${poster})`
          : 'repeating-linear-gradient(135deg, transparent 0 6px, var(--paper-2) 6px 7px)',
        backgroundSize: 'cover', backgroundPosition: 'center' }} />

      {/* Hover clip — fades over the still while active. When there's no poster
          still, the video stays visible so its own first frame IS the idle
          thumbnail (the motion-site behavior); it just plays on hover. */}
      {hasVideo && (
        <video ref={vref} src={m.video} poster={poster || undefined}
          muted loop playsInline preload="metadata" tabIndex={-1}
          style={{ position: 'absolute', inset: 0, width: '100%', height: '100%',
            objectFit: 'cover', opacity: (active || !poster) ? 1 : 0,
            transition: reduce ? 'none' : 'opacity .3s ease' }} />
      )}

      {/* ▶ motion badge — present on any motion slot. Hidden once a real clip is
          actually playing so it doesn't sit on top of the moving frame. */}
      {badge && motion && (
        <div className="mono" aria-hidden="true" style={{ position: 'absolute', top: 7, left: 7,
          display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 9, letterSpacing: 0.6,
          textTransform: 'uppercase', padding: '3px 6px', lineHeight: 1,
          color: 'var(--ink)', background: 'var(--paper-2)', border: '1px solid var(--rule)',
          opacity: (active && hasVideo) ? 0 : 1, transition: 'opacity .2s ease' }}>
          <span style={{ fontSize: 7 }}>{'\u25B6'}</span>
          {hasVideo ? 'motion' : 'motion · clip TBD'}
        </div>
      )}

      {/* Placeholder tag, only when there's nothing to show (no still, no clip). */}
      {showTag && !poster && !hasVideo && (
        <div className="mono" style={{ position: 'absolute', left: 8, bottom: 7, fontSize: 9,
          color: 'var(--ink-3)', letterSpacing: 0.4, textTransform: 'uppercase' }}>
          [{m.tag}]
        </div>
      )}
    </React.Fragment>
  );
}

// ComingSoon — the public "no imagery yet" state. Replaces the striped
// placeholder anywhere a project has no real still/clip yet (project cards, the
// detail image column), so a launched v1.0 reads as intentionally in-progress
// rather than unfinished. Absolute-fill like MotionMedia: the PARENT must be
// position:relative and clip overflow. A motion slot (clip TBD) is NOT "coming
// soon" — it keeps its own ▶ badge via MotionMedia.
function ComingSoon({ label = 'Under construction', sub = 'Imagery coming soon', tag }) {
  return (
    <div style={{ position: 'absolute', inset: 0, background: 'var(--paper-2)',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      gap: 9, textAlign: 'center', padding: 16, boxSizing: 'border-box' }}>
      <span aria-hidden="true" style={{ width: 9, height: 9, border: '1px solid var(--ink-3)',
        transform: 'rotate(45deg)', marginBottom: 1 }} />
      <span className="mono" style={{ fontSize: 10, letterSpacing: 1.4, textTransform: 'uppercase',
        color: 'var(--ink-2)' }}>{label}</span>
      <span className="mono" style={{ fontSize: 9.5, letterSpacing: 0.3, color: 'var(--ink-3)' }}>{sub}</span>
      {tag && (
        <span className="mono" style={{ position: 'absolute', left: 8, bottom: 7, fontSize: 9,
          color: 'var(--ink-3)', letterSpacing: 0.4, textTransform: 'uppercase', opacity: 0.7 }}>[{tag}]</span>
      )}
    </div>
  );
}

// Does this media entry have nothing to show yet (no still, no clip, not even a
// motion slot)? Such entries become ComingSoon on the public site.
function mediaIsPlaceholder(m) { return !(m && (m.src || m.video || m.motion)); }

window.MotionMedia = MotionMedia;
window.ComingSoon = ComingSoon;
window.mediaHasVideo = mediaHasVideo;
window.mediaIsMotion = mediaIsMotion;
window.mediaIsPlaceholder = mediaIsPlaceholder;
