// v5 — Showcase slideshow
// An inline, horizontally scrollable image viewer (drag · arrows · scroll-snap)
// that replaces the single-film Featured block. A curated reel ACROSS clients —
// each client led by its own identity plate (the practice's bespoke wordmark
// treatment, never a borrowed trademark), with Outdoorsy's real artifacts
// threaded in because it's the one engagement with a full public case study.

const SHOW_META = {
  href: "showcase.html",
  // Client roster, in reel order — quiet breadth signal under the lede.
  clients: ["Apple Music", "Outdoorsy", "Poppy", "Somatic"],
};

// `specimen` → render the client's identity plate (V5Specimen). `src` → photo.
// `video` → autoplaying, muted, looping film panel (16:9, full frame).
const SHOW_SLIDES = [
  { client: "Apple Music", year: "2015",    video: "src/assets/apple-music-film.mp4",      label: "Sound, made visible",    note: "Developed a kinetic prototyping system to enliven the art direction.",    kind: "Creative Direction, Digital Product Design" },
  { client: "Outdoorsy",   year: "2022–24", video: "src/assets/outdoorsy-sizzle-film.mp4", label: "The open road, on tap", note: "Directed a brand refresh and deployed it across a from-scratch, token-based design system.",      kind: "Creative Direction, Design System Development" },
  { client: "Poppy",       year: "2024",    video: "src/assets/poppy-film.mp4",            label: "Connect to what matters",     note: "Led product design, humanizing software-connected home goods into warm, everyday objects.",      kind: "Creative Direction, Product Design" },
  { client: "Somatic",     year: "2024",    video: "src/assets/somatic-film.mp4",          label: "Of the body, for the mind",    note: "Built the brand from scratch — naming, identity, packaging and retail — around a holistic, feeling-led experience.",      kind: "Creative Direction, Product Design" },
];

const V5ShowArrow = ({ dir }) => (
  <svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
    {dir === "prev"
      ? <polyline points="15 5 8 12 15 19" />
      : <polyline points="9 5 16 12 9 19" />}
  </svg>
);

const V5Showcase = () => {
  const sectionRef = useRef(null);
  const trackRef = useRef(null);
  const [seen, setSeen] = useState(false);
  const [active, setActive] = useState(0);
  const [progress, setProgress] = useState({ left: 0, width: 12 });
  const [edges, setEdges] = useState({ prev: false, next: true });

  // Entrance — IO with a plain-timeout fallback so the reel never gets
  // stuck hidden if IntersectionObserver is throttled (background tabs, etc.)
  useEffect(() => {
    const el = sectionRef.current;
    if (!el) return;
    const reveal = () => setSeen(true);
    let io;
    if (typeof IntersectionObserver !== "undefined") {
      io = new IntersectionObserver(([e]) => {
        if (e.isIntersecting) { reveal(); io.disconnect(); }
      }, { threshold: 0.12 });
      io.observe(el);
    } else {
      reveal();
    }
    const tick = () => {
      const r = el.getBoundingClientRect();
      if (r.top < window.innerHeight * 1.25 && r.bottom > 0) reveal();
    };
    tick();
    const t = setInterval(tick, 600);
    return () => { if (io) io.disconnect(); clearInterval(t); };
  }, []);

  // Scroll → active index, progress fill, edge states
  const recompute = useCallback(() => {
    const track = trackRef.current;
    if (!track) return;
    const max = track.scrollWidth - track.clientWidth;
    const x = track.scrollLeft;
    const ratio = max > 0 ? x / max : 0;

    const slides = Array.from(track.children);
    const center = x + track.clientWidth / 2;
    let idx = 0, best = Infinity;
    slides.forEach((s, i) => {
      const c = s.offsetLeft + s.offsetWidth / 2;
      const d = Math.abs(c - center);
      if (d < best) { best = d; idx = i; }
    });
    setActive(idx);

    const segW = 100 / slides.length;
    setProgress({ left: ratio * (100 - segW), width: segW });
    setEdges({ prev: x > 4, next: x < max - 4 });
  }, []);

  useEffect(() => {
    const track = trackRef.current;
    if (!track) return;
    recompute();
    track.addEventListener("scroll", recompute, { passive: true });
    window.addEventListener("resize", recompute);
    // Re-measure once images have decoded (widths change → index/progress).
    const imgs = Array.from(track.querySelectorAll("img"));
    imgs.forEach((im) => im.addEventListener("load", recompute));
    // Video slides also change width once their metadata (aspect) is known.
    const vids = Array.from(track.querySelectorAll("video"));
    vids.forEach((v) => v.addEventListener("loadedmetadata", recompute));
    const raf = requestAnimationFrame(recompute);
    const t = setTimeout(recompute, 800);
    return () => {
      track.removeEventListener("scroll", recompute);
      window.removeEventListener("resize", recompute);
      imgs.forEach((im) => im.removeEventListener("load", recompute));
      vids.forEach((v) => v.removeEventListener("loadedmetadata", recompute));
      cancelAnimationFrame(raf);
      clearTimeout(t);
    };
  }, [recompute]);

  const scrollToIdx = useCallback((i) => {
    const track = trackRef.current;
    if (!track) return;
    const slides = Array.from(track.children);
    const clamped = Math.max(0, Math.min(slides.length - 1, i));
    const s = slides[clamped];
    if (!s) return;
    const target = s.offsetLeft - (track.clientWidth - s.offsetWidth) / 2;
    track.scrollTo({ left: target, behavior: "smooth" });
  }, []);

  const step = (dir) => scrollToIdx(active + dir);

  // Drag-to-scroll
  useEffect(() => {
    const track = trackRef.current;
    if (!track) return;
    let down = false, startX = 0, startLeft = 0, moved = 0;

    const onDown = (e) => {
      down = true; moved = 0;
      startX = e.pageX;
      startLeft = track.scrollLeft;
      track.classList.add("is-dragging");
    };
    const onMove = (e) => {
      if (!down) return;
      const dx = e.pageX - startX;
      moved = Math.max(moved, Math.abs(dx));
      track.scrollLeft = startLeft - dx;
    };
    const onUp = () => {
      if (!down) return;
      down = false;
      track.classList.remove("is-dragging");
      requestAnimationFrame(() => {
        const slides = Array.from(track.children);
        const center = track.scrollLeft + track.clientWidth / 2;
        let idx = 0, best = Infinity;
        slides.forEach((s, i) => {
          const c = s.offsetLeft + s.offsetWidth / 2;
          const d = Math.abs(c - center);
          if (d < best) { best = d; idx = i; }
        });
        scrollToIdx(idx);
      });
    };

    track.addEventListener("pointerdown", onDown);
    window.addEventListener("pointermove", onMove);
    window.addEventListener("pointerup", onUp);
    return () => {
      track.removeEventListener("pointerdown", onDown);
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerup", onUp);
    };
  }, [scrollToIdx]);

  // Film slides — play only while on screen (ambient autoplay), pause when
  // scrolled away so the reel never burns CPU/bandwidth in the background.
  useEffect(() => {
    const track = trackRef.current;
    if (!track) return;
    const vids = Array.from(track.querySelectorAll(".v5-show-slide.is-film video"));
    if (!vids.length) return;
    // Hard-guarantee silence: React's `muted` prop doesn't reliably set the DOM
    // property, so force it (plus volume 0) on every film, and re-assert it any
    // time the browser fires a volume change.
    vids.forEach((v) => {
      v.muted = true;
      v.defaultMuted = true;
      v.volume = 0;
      v.setAttribute("muted", "");
      v.addEventListener("volumechange", () => { if (!v.muted || v.volume > 0) { v.muted = true; v.volume = 0; } });
    });
    const safePlay = (v) => { v.muted = true; v.volume = 0; const p = v.play(); if (p && p.catch) p.catch(() => {}); };
    if (typeof IntersectionObserver === "undefined") {
      vids.forEach(safePlay);
      return;
    }
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        const v = e.target;
        if (e.isIntersecting) safePlay(v);
        else if (!v.paused) v.pause();
      });
    }, { threshold: 0.25 });
    vids.forEach((v) => io.observe(v));
    return () => io.disconnect();
  }, []);

  const onKey = (e) => {
    if (e.key === "ArrowRight") { e.preventDefault(); step(1); }
    if (e.key === "ArrowLeft")  { e.preventDefault(); step(-1); }
  };

  const total = SHOW_SLIDES.length;

  return (
    <section
      id="showcase"
      ref={sectionRef}
      className={`v5-show ${seen ? "is-in" : ""}`}
      data-screen-label="Showcase · Selected work">

      <div className="v5-show-orb" aria-hidden="true"></div>

      <div className="v4-container v5-show-container">
        {/* Vertical rail */}
        <div className="v5-show-rail" aria-hidden="true">
          <span className="v5-show-rail-tick"></span>
          <span className="v5-show-rail-text">DESIGN&nbsp;&nbsp;·&nbsp;&nbsp;RESEARCH&nbsp;&nbsp;·&nbsp;&nbsp;SYSTEMS&nbsp;&nbsp;·&nbsp;&nbsp;SIGNAL</span>
        </div>

        {/* Head — airy, two-column. No fixed meta block; the breadth lives
            in the quiet client roster so the space above the reel breathes. */}
        <header className="v5-show-head">
          <div className="v5-show-head-left">
            <div className="v5-show-eyebrow">
              <span className="v5-show-eyebrow-rule"></span>
              <span className="v5-show-eyebrow-num">01</span>
              <span className="v5-show-eyebrow-label">Selected work · 2015–2025</span>
            </div>
          </div>
          <div className="v5-show-head-right">
          </div>
        </header>

        {/* Viewer */}
        <div className={`v5-show-viewer ${edges.prev ? "has-prev" : ""} ${edges.next ? "has-next" : ""}`}>
          <div
            className="v5-show-track"
            ref={trackRef}
            tabIndex={0}
            role="group"
            aria-label="Selected work across clients — drag or use arrow keys to scroll"
            onKeyDown={onKey}>
            {SHOW_SLIDES.map((s, i) => {
              const isSpec = !!s.specimen;
              const isVideo = !!s.video;
              return (
                <figure
                  key={`${s.client}-${s.label}-${i}`}
                  className={`v5-show-slide ${isSpec ? "is-spec" : ""} ${isVideo ? "is-film" : ""}`}
                  style={{ "--idx": i }}
                  data-cursor={`${String(i + 1).padStart(2, "0")} · ${s.client}`}>
                  {isSpec
                    ? <div className="v5-show-slide-spec"><V5Specimen kind={s.specimen} year={s.year} client={s.client} /></div>
                    : isVideo
                    ? <video
                        src={s.video}
                        autoPlay
                        muted
                        loop
                        playsInline
                        preload={i < 3 ? "auto" : "metadata"}
                        disablePictureInPicture
                        aria-label={`${s.client} — ${s.label}`}
                        draggable="false" />
                    : <img src={s.src} alt={`${s.client} — ${s.label}`} loading={i < 3 ? "eager" : "lazy"} draggable="false" />}
                  <div className="v5-show-slide-top">
                    <span className="v5-show-slide-n">{String(i + 1).padStart(2, "0")} / {String(total).padStart(2, "0")}</span>
                    <span className="v5-show-slide-kind">{s.kind}</span>
                  </div>
                  <figcaption className="v5-show-slide-cap">
                    <span className="v5-show-slide-client">{s.client}</span>
                    <span className="v5-show-slide-label">{s.label}</span>
                    <span className="v5-show-slide-note">{s.note}</span>
                  </figcaption>
                </figure>
              );
            })}
          </div>
        </div>

        {/* Controls */}
        <div className="v5-show-controls">
          <span className="v5-show-counter">
            <span className="cur">{String(active + 1).padStart(2, "0")}</span>
            <span className="tot"> / {String(total).padStart(2, "0")}</span>
          </span>
          <div className="v5-show-rail-track" aria-hidden="true">
            <span className="v5-show-rail-fill" style={{ left: `${progress.left}%`, width: `${progress.width}%` }}></span>
          </div>
          <div className="v5-show-arrows">
            <button
              type="button"
              className="v5-show-arrow"
              onClick={() => step(-1)}
              disabled={!edges.prev}
              aria-label="Previous image"
              data-cursor="Previous">
              <V5ShowArrow dir="prev" />
            </button>
            <button
              type="button"
              className="v5-show-arrow"
              onClick={() => step(1)}
              disabled={!edges.next}
              aria-label="Next image"
              data-cursor="Next">
              <V5ShowArrow dir="next" />
            </button>
          </div>
        </div>

      </div>
    </section>
  );
};

Object.assign(window, { V5Showcase });
