// components-placeholders.jsx — Image placeholder ("PicSlot") used across the page.
// Renders <img src="..."> only when the path is in window.UPLOADED_IMAGES
// (so missing files don't pollute the console). Add a filename to the set
// when the user uploads it.

// ─── Manifest of uploaded images. Populate as the user uploads. ────────
window.UPLOADED_IMAGES = window.UPLOADED_IMAGES || new Set([
  "uploads/fenwei1.png",
  "uploads/fenwei2.png",
  "uploads/hero.png",
  "uploads/seal-bi.png",
  "uploads/bg-rainy-guest.png",
  // Screen placeholders — drop real screenshots in and uncomment.
  "uploads/scr-hero.webp",
  "uploads/scr-overview.webp",
  // "uploads/scr-world.webp",
  "uploads/scr-characters.webp",
  // "uploads/scr-objects.webp",
  // "uploads/scr-events.webp",
  // "uploads/scr-writing.webp",
]);

function isUploaded(src) {
  return !!src && window.UPLOADED_IMAGES && window.UPLOADED_IMAGES.has(src);
}

function PicSlot({
  src,
  alt = "",
  label,                 // human label, e.g. "Hero · 主视觉"
  hint,                  // one-line description of what goes here
  aspect = "16/10",      // CSS aspect-ratio string
  variant = "product",   // "product" | "mood" | "portrait" | "map" | "desk"
  className = "",
  style = {},
}) {
  const [errored, setErrored] = React.useState(false);
  const [loaded, setLoaded]   = React.useState(false);
  const showImg = isUploaded(src) && !errored;

  return (
    <div
      className={`pic-slot pic-slot--${variant} ${className}`}
      data-loaded={loaded ? "1" : "0"}
      style={{ aspectRatio: aspect, ...style }}
    >
      {showImg && (
        <img
          src={src}
          alt={alt || label || ""}
          onLoad={() => setLoaded(true)}
          onError={() => setErrored(true)}
        />
      )}

      {(!showImg || !loaded) && (
        <div className="pic-slot__plate" aria-hidden={loaded ? "true" : "false"}>
          <span className="pic-slot__corner pic-slot__corner--tl" />
          <span className="pic-slot__corner pic-slot__corner--tr" />
          <span className="pic-slot__corner pic-slot__corner--bl" />
          <span className="pic-slot__corner pic-slot__corner--br" />

          <div className="pic-slot__meta">
            <div className="pic-slot__tag">
              <span className="pic-slot__dot" />
              {variantLabel(variant)}
            </div>
            {label && <div className="pic-slot__label">{label}</div>}
            {hint && <div className="pic-slot__hint">{hint}</div>}
            {src && (
              <div className="pic-slot__path">
                <span className="pic-slot__path-arrow">⤓</span>
                <code>{src}</code>
              </div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

function variantLabel(v) {
  return {
    product:  "PRODUCT SHOT",
    mood:     "MOOD IMAGE",
    portrait: "INK PORTRAIT",
    map:      "MAP / ATLAS",
    desk:     "FULL DESK SHOT",
  }[v] || "IMAGE";
}

/* ────────────────────────────────────────────────────────────
   SceneDivider — full-bleed cinematic banner between sections.
   Used to break the page rhythm with an atmospheric image
   and a short literal excerpt from the demo book.
   ──────────────────────────────────────────────────────────── */
function SceneDivider({
  src,
  alt = "",
  chapter,             // small uppercase label (e.g. "雨中客 · 卷一 · 第 1 章")
  quote,               // one short line, taken literally from the prose
  side = "left",       // caption alignment: "left" | "right"
  height = "62vh",     // banner height; clamped in CSS
}) {
  const exists = isUploaded(src);
  return (
    <section className="scene-divider" aria-label={alt || chapter || quote} style={{ "--sd-h": height }}>
      <div className="scene-divider__media">
        {exists
          ? <img src={src} alt={alt} />
          : <div className="scene-divider__placeholder">
              <div className="pic-slot__tag"><span className="pic-slot__dot" />MOOD BANNER</div>
              <div className="pic-slot__path"><code>{src}</code></div>
            </div>}
        <div className="scene-divider__veil" data-side={side} />
      </div>
      <div className={`scene-divider__cap scene-divider__cap--${side}`}>
        {chapter && <div className="scene-divider__chapter">{chapter}</div>}
        {quote && <blockquote className="scene-divider__quote">{quote}</blockquote>}
      </div>
    </section>
  );
}

/* ────────────────────────────────────────────────────────────
   Divider — a small set of decorative dividers, one per variant.
   Place between sections, never two in a row.
   ──────────────────────────────────────────────────────────── */
function Divider({ variant = "brush", label, lang = "zh" }) {
  return (
    <div className={`divider divider--${variant}`} role="presentation">
      {variant === "brush"   && <DividerBrush />}
      {variant === "dots"    && <DividerDots />}
      {variant === "seal"    && <DividerSeal lang={lang} />}
      {variant === "chapter" && <DividerChapter label={label} />}
      {variant === "wave"    && <DividerWave />}
    </div>
  );
}

// A · 水墨笔触
function DividerBrush() {
  return (
    <svg viewBox="0 0 360 24" preserveAspectRatio="xMidYMid meet" aria-hidden="true">
      <defs>
        <linearGradient id="brush-grad" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0"   stopColor="#6F4A2B" stopOpacity="0" />
          <stop offset="0.1" stopColor="#6F4A2B" stopOpacity=".55" />
          <stop offset="0.5" stopColor="#3A2412" stopOpacity=".9" />
          <stop offset="0.9" stopColor="#6F4A2B" stopOpacity=".5" />
          <stop offset="1"   stopColor="#6F4A2B" stopOpacity="0" />
        </linearGradient>
      </defs>
      {/* main stroke */}
      <path
        d="M 12 12.4 C 60 9.8, 110 14.6, 170 11.8 C 220 9.8, 270 13.6, 348 11.2"
        stroke="url(#brush-grad)" strokeWidth="2.2" fill="none" strokeLinecap="round"
      />
      {/* flying-white wisps */}
      <path d="M 56 13.4 C 84 12.8, 108 13.2, 132 12.6" stroke="#3A2412" strokeOpacity=".22" strokeWidth=".6" fill="none" strokeLinecap="round"/>
      <path d="M 180 11.6 C 210 11.0, 246 12.0, 280 11.2" stroke="#3A2412" strokeOpacity=".22" strokeWidth=".6" fill="none" strokeLinecap="round"/>
      {/* a small ink dot at the tip */}
      <circle cx="348" cy="11" r="1.4" fill="#3A2412" opacity=".55"/>
    </svg>
  );
}

// B · 三点
function DividerDots() {
  return (
    <div className="divider-dots__row">
      <span className="divider-dots__line" />
      <span className="divider-dots__dot" />
      <span className="divider-dots__dot" />
      <span className="divider-dots__dot" />
      <span className="divider-dots__line" />
    </div>
  );
}

// C · 印章 — uses the real seal PNG (transparent BG)
function DividerSeal({ lang }) {
  return (
    <div className="divider-seal__row">
      <span className="divider-seal__line" />
      <img
        className="divider-seal__stamp"
        src="uploads/seal-bi.png"
        alt={lang === "zh" ? "笔随印章" : "Novel Studio seal"}
        draggable="false"
      />
      <span className="divider-seal__line" />
    </div>
  );
}

// D · 章节标记
function DividerChapter({ label }) {
  return (
    <div className="divider-chapter__row">
      <span className="divider-chapter__line" />
      <span className="divider-chapter__label">· {label || "笔随 · Novel Studio"} ·</span>
      <span className="divider-chapter__line" />
    </div>
  );
}

// E (bonus) · 古书云纹波浪
function DividerWave() {
  return (
    <svg viewBox="0 0 360 16" aria-hidden="true">
      <path
        d="M 12 8 C 30 2, 50 14, 70 8 C 90 2, 110 14, 130 8 C 150 2, 170 14, 190 8 C 210 2, 230 14, 250 8 C 270 2, 290 14, 310 8 C 330 2, 350 14, 360 8"
        stroke="#6F4A2B" strokeOpacity=".6" strokeWidth="1.2" fill="none" strokeLinecap="round"
      />
    </svg>
  );
}

/* ────────────────────────────────────────────────────────────
   Zoomable — click an image to open a fullscreen lightbox.
   Click backdrop (or press Esc) to dismiss. Locks body scroll
   and signals fullpage-scroll.js to pause its wheel hijack via
   the `lightbox-open` class on <body>. Reusable for all screen
   screenshots as they replace mocks.
   ──────────────────────────────────────────────────────────── */
function Zoomable({ src, alt = "", className = "", imgProps = {} }) {
  const [open, setOpen] = React.useState(false);

  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("keydown", onKey);
    // Class signals to fullpage-scroll.js + locks underlying scroll.
    document.body.classList.add("lightbox-open");
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.removeEventListener("keydown", onKey);
      document.body.classList.remove("lightbox-open");
      document.body.style.overflow = prevOverflow;
    };
  }, [open]);

  return (
    <React.Fragment>
      <img
        className={className}
        src={src}
        alt={alt}
        onClick={() => setOpen(true)}
        {...imgProps}
      />
      {open && (
        <div
          className="lightbox"
          role="dialog"
          aria-modal="true"
          aria-label={alt || "放大查看"}
          onClick={() => setOpen(false)}
        >
          <img
            className="lightbox__img"
            src={src}
            alt={alt}
            onClick={(e) => e.stopPropagation()}
          />
          <button
            type="button"
            className="lightbox__close"
            aria-label="关闭"
            onClick={() => setOpen(false)}
          >
            <svg viewBox="0 0 24 24" width="18" height="18" fill="none"
                 stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M6 6l12 12M18 6L6 18" />
            </svg>
          </button>
        </div>
      )}
    </React.Fragment>
  );
}

Object.assign(window, { PicSlot, SceneDivider, Divider, Zoomable });
