/* ============================================================
   SHARED COMPONENTS
   ============================================================ */
const { useState, useEffect, useRef, useCallback } = React;

/* ---------- icons ---------- */
function ArrowDiag() {
  return (
    <svg viewBox="0 0 16 16" fill="none" width="100%" height="100%">
      <path d="M4 12L12 4M12 4H5M12 4V11" stroke="currentColor" strokeWidth="1.5" strokeLinecap="square"/>
    </svg>
  );
}
function ArrowRight({ s = 16 }) {
  return (
    <svg viewBox="0 0 24 16" fill="none" width={s} height={s * 0.66}>
      <path d="M0 8H22M22 8L15 1M22 8L15 15" stroke="currentColor" strokeWidth="1.5"/>
    </svg>
  );
}

/* sliding double-arrow link */
function ArrowLink({ children, onClick, href }) {
  const Tag = href ? "a" : "button";
  return (
    <Tag className="arrow-link" onClick={onClick} href={href}>
      <span>{children}</span>
      <span className="arw"><ArrowDiag/><ArrowDiag/></span>
    </Tag>
  );
}

/* ---------- media plate (placeholder OR real screenshot) ---------- */
function Plate({
  tint,
  accent,
  visual = "mesh",
  label,
  corner,
  video,
  img,
  fit,
  position,
  objectFit,
  objectPosition,
  alt,
  className = "",
  style = {},
  onUnmute,
  muted
}) {
  // parse lightness from the oklch tint to choose contrast
  const L = parseFloat((String(tint).match(/[\d.]+/) || [0.8])[0]);
  const dark = L < 0.6;
  const shot = img && typeof img === "object" ? img : { src: img };
  const hasImg = Boolean(shot && shot.src);
  const fitValues = ["cover", "contain", "fill", "none", "scale-down"];
  const fitIsObjectFit = fitValues.includes(fit);
  const imgFit = shot.fit || objectFit || (fitIsObjectFit ? fit : "cover");
  const imgPos = shot.position || shot.objectPosition || objectPosition || position || (!fitIsObjectFit ? fit : null) || "left top";
  const imgPad = shot.pad || "0px";
  const imgBg = shot.background || "transparent";
  const variant = shot.variant ? " plate-" + shot.variant : "";
  return (
    <div
      className={"plate " + (dark ? "plate-dark " : "plate-light ") + (hasImg ? "plate-img " : "") + variant + " " + className}
      style={{
        "--tint": tint,
        "--accent": accent || "oklch(0.46 0.03 70)",
        "--img-fit": imgFit,
        "--img-pos": imgPos,
        "--img-pad": imgPad,
        "--img-bg": imgBg,
        ...style
      }}
    >
      {hasImg && <img className="plate-photo" src={shot.src} alt={shot.alt || alt || label || ""} />}
      {!hasImg && <PlateVisual visual={visual} />}
      {corner && <div className="plate-corner">{corner}</div>}
      {label && <div className="plate-label">{label}</div>}
      {video && (
        <button className="pill" onClick={(e) => { e.stopPropagation(); onUnmute && onUnmute(); }}>
          <span className="dot"></span>{muted ? "Unmute" : "Mute"}
        </button>
      )}
    </div>
  );
}

function PlateVisual({ visual }) {
  return (
    <div className={"plate-visual pv-" + visual} aria-hidden="true">
      <span className="pv-glow pv-g1"></span>
      <span className="pv-glow pv-g2"></span>
      <span className="pv-rule pv-r1"></span>
      <span className="pv-rule pv-r2"></span>
      <span className="pv-rule pv-r3"></span>
      <span className="pv-node pv-n1"></span>
      <span className="pv-node pv-n2"></span>
      <span className="pv-node pv-n3"></span>
      <span className="pv-node pv-n4"></span>
      <span className="pv-card pv-c1"></span>
      <span className="pv-card pv-c2"></span>
      <span className="pv-card pv-c3"></span>
      <span className="pv-ring pv-ring-1"></span>
      <span className="pv-ring pv-ring-2"></span>
      <span className="pv-wave pv-w1"></span>
      <span className="pv-wave pv-w2"></span>
    </div>
  );
}

/* ---------- split-text reveal ---------- */
/* renders text as masked rows of words that slide up when .is-in is set on a parent */
function SplitWords({ text, className = "", el = "span" }) {
  const words = String(text).split(" ");
  const El = el;
  return (
    <El className={className}>
      {words.map((w, i) => (
        <span className="reveal-mask" key={i} style={{ marginRight: "0.28em" }}>
          <span className="reveal-word" style={{ animationDelay: (i * 0.05) + "s" }}>{w}</span>
        </span>
      ))}
    </El>
  );
}

/* ---------- intersection reveal wrapper ---------- */
/* NOTE: IntersectionObserver does not fire reliably in this preview
   iframe, so we use a scroll + rAF rect check instead. */
function Reveal({ children, className = "", as = "div", once = true, style = {} }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  const [shown, setShown] = useState(false); // fallback: force-visible if anim never plays
  useEffect(() => {
    const node = ref.current;
    if (!node) return;
    let done = false;
    let fallback = null;
    const check = () => {
      if (done) return;
      const r = node.getBoundingClientRect();
      const vh = window.innerHeight || document.documentElement.clientHeight;
      if (r.top < vh * 0.9 && r.bottom > 0) {
        setSeen(true);
        fallback = setTimeout(() => setShown(true), 1500);
        if (once) { done = true; cleanup(); }
      } else if (!once) {
        setSeen(false);
      }
    };
    const cleanup = () => {
      window.removeEventListener("scroll", check);
      window.removeEventListener("resize", check);
    };
    window.addEventListener("scroll", check, { passive: true });
    window.addEventListener("resize", check);
    const r1 = requestAnimationFrame(() => requestAnimationFrame(check));
    const t = setTimeout(check, 140);
    return () => { cleanup(); cancelAnimationFrame(r1); clearTimeout(t); if (fallback) clearTimeout(fallback); };
  }, [once]);
  const As = as;
  return (
    <As ref={ref} className={className + (seen ? " is-in" : "") + (shown ? " rv-shown" : "")} style={style}>
      {children}
    </As>
  );
}

/* ============================================================
   HEADER
   ============================================================ */
function Header({ route, go, openContact, blend }) {
  const onHome = route.page === "home";
  const nav = [
    { id: "home", label: "Index" },
    { id: "research", label: "Research" },
    { id: "lab", label: "Lab" },
    { id: "about", label: "About" },
  ];
  return (
    <header className={"site-header" + (blend ? " blend" : "")}>
      <button className="brand" onClick={() => go({ page: "home" })}>
        {onHome
          ? <span>Security, Research, <span className="serif">Architecture</span></span>
          : <span>Dylan <span className="serif">jeppesen</span></span>}
      </button>
      <nav className="nav">
        {nav.map((n, i) => (
          <React.Fragment key={n.id}>
            <a
              className={route.page === n.id || (n.id === "home" && route.page === "case") ? "active" : ""}
              onClick={(e) => {
                e.preventDefault();
                if (n.id === "research" || n.id === "lab") { go({ page: "home" }); }
                else go({ page: n.id });
              }}
              href="#"
            >{n.label}</a>
            {i < nav.length - 1 && <span className="sep">,</span>}
          </React.Fragment>
        ))}
      </nav>
      <button className="ul" onClick={openContact}>Send me a message</button>
    </header>
  );
}

Object.assign(window, { ArrowDiag, ArrowRight, ArrowLink, Plate, SplitWords, Reveal, Header });
