/* updates-views.jsx — the Updates feed: Facebook-style status posts with a
   photo grid (1/2/3/4+ with "+N") and a full-screen lightbox.
   Data from window.UPDATES. Depends on Reveal (views.jsx) and AUTHOR/fmtDate
   are re-declared here to stay self-contained. */

const { useState: useUS, useEffect: useUE, useCallback: useUC } = React;

const UP_AUTHOR = {
  name: "Muhamad Firdaus Ali",
  avatar: "assets/avatar.png?v=2",
};

function updDate(iso) {
  const [y, m, d] = (iso || "").split("-").map(Number);
  if (!y) return iso || "";
  const months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  return `${months[(m || 1) - 1]} ${d}, ${y}`;
}

/* ---------- photo grid: FB-style tiling by count ---------- */
function PhotoGrid({ photos, onOpen }) {
  const n = photos.length;
  if (n === 0) return null;
  // cap visible tiles at 4; the 4th carries a "+N" overlay when there are more
  const tiles = photos.slice(0, 4);
  const extra = n - 4;
  const layout = n === 1 ? "one" : n === 2 ? "two" : n === 3 ? "three" : "four";

  return (
    <div className={`upd-photos upd-photos-${layout}`}>
      {tiles.map((src, i) => {
        const isLast = i === 3 && extra > 0;
        return (
          <button key={i} className="upd-photo" onClick={() => onOpen(i)}
            style={{ backgroundImage: `url("${src}")` }} aria-label={`Photo ${i + 1}`}>
            {isLast && <span className="upd-photo-more">+{extra}</span>}
          </button>
        );
      })}
    </div>
  );
}

/* ---------- one status post ---------- */
function UpdateCard({ update, onOpenPhoto }) {
  return (
    <article className="upd-card">
      <header className="upd-head">
        <img className="upd-avatar" src={UP_AUTHOR.avatar} alt="" />
        <div className="upd-meta">
          <span className="upd-name">{UP_AUTHOR.name}</span>
          <span className="upd-time">
            {updDate(update.date)}{update.place ? ` · ${update.place}` : ""}
          </span>
        </div>
      </header>
      {update.text && <p className="upd-text">{update.text}</p>}
      {update.photos && update.photos.length > 0 && (
        <PhotoGrid photos={update.photos} onOpen={(i) => onOpenPhoto(update.photos, i)} />
      )}
    </article>
  );
}

/* ---------- lightbox ---------- */
function Lightbox({ photos, index, onClose, onNav }) {
  useUE(() => {
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
      else if (e.key === "ArrowRight") onNav(1);
      else if (e.key === "ArrowLeft") onNav(-1);
    };
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      document.body.style.overflow = "";
    };
  }, [onClose, onNav]);

  // touch gestures: horizontal = prev/next, swipe-down = dismiss. The image follows
  // the finger for a native feel, then snaps or commits on release.
  const drag = React.useRef({ x: 0, y: 0, active: false });
  const [off, setOff] = React.useState({ x: 0, y: 0 });
  const onTouchStart = (e) => {
    const t = e.touches[0];
    drag.current = { x: t.clientX, y: t.clientY, active: true };
  };
  const onTouchMove = (e) => {
    if (!drag.current.active) return;
    const t = e.touches[0];
    setOff({ x: t.clientX - drag.current.x, y: t.clientY - drag.current.y });
  };
  const onTouchEnd = () => {
    const { x, y } = off;
    drag.current.active = false;
    if (Math.abs(y) > 90 && Math.abs(y) > Math.abs(x)) { onClose(); return; }
    if (Math.abs(x) > 60 && Math.abs(x) > Math.abs(y)) { onNav(x < 0 ? 1 : -1); }
    setOff({ x: 0, y: 0 });
  };

  if (index == null) return null;
  const many = photos.length > 1;
  const dragStyle = (off.x || off.y)
    ? { transform: `translate(${off.x}px, ${off.y}px)`, transition: "none",
        opacity: 1 - Math.min(Math.abs(off.y) / 400, 0.5) }
    : { transition: "transform .25s ease, opacity .25s ease" };
  // Portal to <body> — the feed lives inside a transformed .view-wrap, and a CSS
  // transform on an ancestor breaks position:fixed (it becomes relative to that
  // ancestor). Rendering into body keeps the overlay truly viewport-fixed & centered.
  const node = (
    <div className="lightbox" onClick={onClose} role="dialog" aria-modal="true"
      onTouchStart={onTouchStart} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd}>
      <button className="lightbox-close" onClick={onClose} aria-label="Close">×</button>
      {many && (
        <button className="lightbox-nav prev"
          onClick={(e) => { e.stopPropagation(); onNav(-1); }} aria-label="Previous">‹</button>
      )}
      <img className="lightbox-img" src={photos[index]} alt="" style={dragStyle}
        onClick={(e) => e.stopPropagation()} draggable="false" />
      {many && (
        <button className="lightbox-nav next"
          onClick={(e) => { e.stopPropagation(); onNav(1); }} aria-label="Next">›</button>
      )}
      {many && <span className="lightbox-count">{index + 1} / {photos.length}</span>}
      {many && <span className="lightbox-hint">swipe · tap to close</span>}
    </div>
  );
  return ReactDOM.createPortal(node, document.body);
}

/* ---------- the Updates feed ---------- */
function UpdatesView() {
  const updates = (window.UPDATES || []).slice();
  // defensive: sort newest-first by date
  updates.sort((a, b) => (a.date < b.date ? 1 : a.date > b.date ? -1 : 0));

  const [box, setBox] = useUS({ photos: null, index: null });
  const openPhoto = useUC((photos, index) => setBox({ photos, index }), []);
  const closePhoto = useUC(() => setBox({ photos: null, index: null }), []);
  const nav = useUC((dir) => {
    setBox((b) => {
      if (b.index == null) return b;
      const len = b.photos.length;
      return { ...b, index: (b.index + dir + len) % len };
    });
  }, []);

  return (
    <div className="view updates">
      <Reveal as="header" className="upd-header">
        <h1 className="blog-title">Updates</h1>
        <p className="blog-sub">
          Occasional notes and photographs — what I've been doing, reading, and
          building. Posted when there's something worth saying.
        </p>
      </Reveal>

      <div className="upd-feed">
        {updates.length === 0 ? (
          <p className="blog-sub">Nothing posted yet.</p>
        ) : (
          updates.map((u, i) => (
            <Reveal key={`${u.date}-${i}`} delay={Math.min(i, 5) * 45}>
              <UpdateCard update={u} onOpenPhoto={openPhoto} />
            </Reveal>
          ))
        )}
      </div>

      <Reveal as="footer" className="upd-foot"><span className="foot-mark">了</span></Reveal>

      {box.index != null && (
        <Lightbox photos={box.photos} index={box.index} onClose={closePhoto} onNav={nav} />
      )}
    </div>
  );
}

Object.assign(window, { UpdatesView });
