// Cases are loaded from cases.json. Each entry has:
//   `picturePreview`: image URL shown immediately while the video downloads
//   `videoPreview`:   optional video URL — once it finishes downloading,
//                     it replaces the picture preview and plays on loop
// A neutral fallback tile is shown if media fails to load.
// A per-page-load cache buster appended to every media URL so the browser
// can't serve a stale empty/404 from a previous visit. cases.json itself is
// already fetched with no-cache.
window.__mediaBust = String(Date.now());

function bust(url) {
  if (!url) return url;
  // Leave absolute/external URLs alone — only bust same-origin uploads.
  if (/^https?:\/\//i.test(url)) return url;
  const sep = url.indexOf("?") === -1 ? "?" : "&";
  return url + sep + "v=" + window.__mediaBust;
}

window.loadWorks = function loadWorks() {
  return fetch("cases.json", { cache: "no-cache" }).then((r) => {
    if (!r.ok) throw new Error("Failed to load cases.json: " + r.status);
    return r.json();
  });
};


// Tag list is no longer hardcoded here — filters are now derived from
// the actual tags present in cases.json (see Works in app.jsx).

// Real media tile. If the image/video fails to load (e.g. missing upload),
// fall back to a neutral swatch tile so the card doesn't render a broken icon.
// When `work.videoPreview` is set, the picture is shown first while the video
// downloads in the background; once the video is ready it fades in over the
// image and loops silently. Otherwise the image alone is shown.
function WorkPreview({ work }) {
  const [imgFailed, setImgFailed] = React.useState(false);
  const [videoFailed, setVideoFailed] = React.useState(false);
  const [videoReady, setVideoReady] = React.useState(false);
  const videoRef = React.useRef(null);

  // Once the video has buffered enough to play through without stalling,
  // start it and reveal it over the static image.
  const handleVideoReady = React.useCallback(() => {
    const v = videoRef.current;
    if (!v) return;
    const p = v.play();
    if (p && typeof p.catch === "function") p.catch(() => {});
    setVideoReady(true);
  }, []);

  if (work.picturePreview && !imgFailed) {
    const hasVideo = !!work.videoPreview && !videoFailed;
    return (
      <div className="aspect-[4/3] w-full bg-[#F4F2EE] overflow-hidden relative">
        <img
          src={bust(work.picturePreview)}
          alt={work.title}
          onError={() => setImgFailed(true)}
          className="absolute inset-0 w-full h-full object-cover" />
        {hasVideo &&
        <video
          ref={videoRef}
          src={bust(work.videoPreview)}
          muted
          loop
          playsInline
          autoPlay
          preload="auto"
          onCanPlayThrough={handleVideoReady}
          onLoadedData={handleVideoReady}
          onError={() => setVideoFailed(true)}
          className="absolute inset-0 w-full h-full object-cover transition-opacity duration-300"
          style={{ opacity: videoReady ? 1 : 0 }} />
        }
      </div>);
  }

  return <div className="aspect-[4/3] w-full" style={{ background: work.swatch || "#F4F2EE" }} />;
}

function TelegramIcon() {
  return (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" style={{ width: "16px", height: "16px" }}>
      <path d="M21.95 4.36 18.7 19.69c-.24 1.08-.88 1.34-1.78.84l-4.92-3.63-2.37 2.28c-.26.26-.48.48-.99.48l.35-5.02 9.13-8.25c.4-.35-.09-.55-.61-.2L6.22 13.3 1.37 11.78c-1.05-.33-1.07-1.05.22-1.55L20.59 2.9c.88-.33 1.65.2 1.36 1.46Z" style={{ fill: "rgb(154, 154, 154)" }} />
    </svg>);

}

function ExternalIcon() {
  return (
    <svg width="10" height="10" viewBox="0 0 10 10" fill="none" aria-hidden="true">
      <path d="M3 1H9V7" stroke="currentColor" strokeWidth="1.2" strokeLinecap="square" />
      <path d="M9 1L1 9" stroke="currentColor" strokeWidth="1.2" strokeLinecap="square" />
    </svg>);

}

function WorkCard({ work }) {
  const [hover, setHover] = React.useState(false);
  return (
    <a
      href={work.link.href}
      target="_blank"
      rel="noopener noreferrer"
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      onFocus={() => setHover(true)}
      onBlur={() => setHover(false)}
      className="group flex flex-col border border-[#EFEFEF] rounded-[6px] overflow-hidden bg-white transition-colors duration-200 hover:border-[#111] no-underline">
      
      <div className="overflow-hidden">
        <div className="transition-transform duration-300 ease-out group-hover:scale-[1.015]">
          <WorkPreview work={work} />
        </div>
      </div>
      <div className="p-4 flex flex-col gap-3 flex-1" style={{ padding: "12px 16px 16px" }}>
        <div className="flex flex-col gap-1">
          <h3 className="text-[15px] font-medium tracking-[-0.01em] text-[#111]">{work.title}</h3>
          <p className="text-[13px] leading-snug text-[#6B6B6B] text-pretty">{work.desc}</p>
        </div>
        <div className="mt-auto font-mono text-[10px] uppercase tracking-wider text-[#6B6B6B]" style={{ wordSpacing: "-0.15em" }}>
          {`TYPE: ${work.tags.join(", ")}`}
        </div>
        <span
          className="inline-flex items-center gap-1.5 text-[12px] font-medium text-[#111] underline-offset-4 group-hover:underline w-fit">
          
          {work.link.label}
          <ExternalIcon />
        </span>
      </div>
    </a>);

}

// Terminal card for the end of the grid. Matches the card frame
// (border, radius, background) of WorkCard but has no preview, no hover
// effect, and centers its content. Typography mirrors:
//   - title style → "More work will be added over time."
//   - tag style   → "Last update: May 14, 2026"
function EndCard() {
  return (
    <div
      className="flex flex-col items-center justify-center text-center border border-[#EFEFEF] rounded-[6px] bg-white h-full"
      style={{ padding: "24px 20px", minHeight: "100%", backgroundColor: "rgb(248, 248, 248)" }}>
      <h3 className="text-[15px] font-medium tracking-[-0.01em] text-[#111] text-pretty max-w-[22ch]">
        More work will be added over time.
      </h3>
      <div
        className="font-mono text-[10px] uppercase tracking-wider text-[#6B6B6B] mt-3"
        style={{ wordSpacing: "-0.15em" }}>
        Last update: May 14, 2026
      </div>
    </div>);

}

window.WorkCard = WorkCard;
window.EndCard = EndCard;
window.ExternalIcon = ExternalIcon;
window.TelegramIcon = TelegramIcon;