/* global React */
// Shared atomic components reused across pages.

const { useEffect, useState, useRef } = React;

// ── Nav ─────────────────────────────────────────────────────
function Nav({ active, onNav, current = "home" }) {
  const [time, setTime] = useState(() => fmtTime(new Date()));
  const [progress, setProgress] = useState(0);
  useEffect(() => {
    const t = setInterval(() => setTime(fmtTime(new Date())), 1000);
    return () => clearInterval(t);
  }, []);
  useEffect(() => {
    let raf = 0;
    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(() => {
        const d = document.documentElement;
        const max = (d.scrollHeight - d.clientHeight) || 1;
        setProgress(Math.min(1, Math.max(0, d.scrollTop / max)));
        raf = 0;
      });
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
    };
  }, [current]);
  return (
    <nav className="nav">
      <div className="nav-progress" aria-hidden="true">
        <div className="nav-progress-fill" style={{ transform: `scaleX(${progress})` }} />
      </div>
      <div className="container">
        <div className="nav-inner">
          <button
            className="row gap-3"
            onClick={() => onNav?.("home")}
            style={{ alignItems: "center" }}
            aria-label="Benjamin Dison — home"
          >
            <span className="nav-mark">BD</span>
            <span className="nav-mono">
              <b>Benjamin Dison</b>&nbsp;&nbsp;Mechanical Engineering Portfolio
            </span>
          </button>
          <div className="nav-links">
            {["projects", "experience", "about", "contact"].map((k) => (
              <a
                key={k}
                href={`#${k}`}
                className={active === k ? "is-active" : ""}
                onClick={(e) => {
                  if (current !== "home") {
                    e.preventDefault();
                    onNav?.("home", k);
                  }
                }}
              >
                {k}
              </a>
            ))}
            <span className="nav-mono tabular" aria-hidden="true">{time} MT</span>
            <ThemeToggle />
          </div>
        </div>
      </div>
    </nav>
  );
}

function fmtTime(d) {
  const h = String(d.getHours()).padStart(2, "0");
  const m = String(d.getMinutes()).padStart(2, "0");
  const s = String(d.getSeconds()).padStart(2, "0");
  return `${h}:${m}:${s}`;
}

// ── Theme toggle (dark / light) ─────────────────────────────
function ThemeToggle() {
  const [theme, setTheme] = useState(() =>
    document.documentElement.getAttribute("data-palette") || "pure-dark"
  );
  const isLight = theme === "light";
  function toggle() {
    const next = isLight ? "pure-dark" : "light";
    document.documentElement.setAttribute("data-palette", next);
    setTheme(next);
    try { localStorage.setItem("bd-palette", next); } catch (_) {}
  }
  useEffect(() => {
    try {
      const saved = localStorage.getItem("bd-palette");
      if (saved) {
        document.documentElement.setAttribute("data-palette", saved);
        setTheme(saved);
      }
    } catch (_) {}
  }, []);
  return (
    <button
      className="theme-toggle"
      onClick={toggle}
      aria-label={`Switch to ${isLight ? "dark" : "light"} mode`}
      title={`${isLight ? "Dark" : "Light"} mode`}
    >
      <span className="icon dk">DK</span>
      <span className="icon lt">LT</span>
      <span className="knob" />
    </button>
  );
}

// ── Image frame (with caption + hover reveal + mouse parallax) ───
function ImgFrame({ src, alt, caption, label, ratio, className = "", parallax = true }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!parallax) return;
    const el = ref.current;
    if (!el) return;
    const target = el.querySelector("img, .placeholder");
    if (!target) return;
    let raf = 0;
    let tx = 0, ty = 0, cur = { x: 0, y: 0 };
    const STRENGTH = 14; // px max travel
    const onMove = (e) => {
      const rect = el.getBoundingClientRect();
      // -1..1 from element center
      const px = ((e.clientX - rect.left) / rect.width - 0.5) * 2;
      const py = ((e.clientY - rect.top) / rect.height - 0.5) * 2;
      tx = -px * STRENGTH;
      ty = -py * STRENGTH;
      if (!raf) tick();
    };
    const onLeave = () => { tx = 0; ty = 0; if (!raf) tick(); };
    const tick = () => {
      cur.x += (tx - cur.x) * 0.12;
      cur.y += (ty - cur.y) * 0.12;
      target.style.setProperty("--p-x", cur.x.toFixed(2) + "px");
      target.style.setProperty("--p-y", cur.y.toFixed(2) + "px");
      target.style.setProperty("--p-s", "1.06");
      if (Math.abs(tx - cur.x) > 0.05 || Math.abs(ty - cur.y) > 0.05) {
        raf = requestAnimationFrame(tick);
      } else {
        raf = 0;
      }
    };
    el.addEventListener("mousemove", onMove);
    el.addEventListener("mouseleave", onLeave);
    return () => {
      el.removeEventListener("mousemove", onMove);
      el.removeEventListener("mouseleave", onLeave);
      cancelAnimationFrame(raf);
    };
  }, [parallax, src]);
  return (
    <div ref={ref} className={`img-frame ${className}`} style={ratio ? { aspectRatio: ratio } : null}>
      {src ? (
        <img src={src} alt={alt || ""} loading="lazy" />
      ) : (
        <div className="placeholder">
          <span className="placeholder-label">{label || "Image · placeholder"}</span>
        </div>
      )}
      {caption ? <div className="img-meta">{caption}</div> : null}
    </div>
  );
}

// ── Tags ────────────────────────────────────────────────────
function Tags({ items }) {
  return (
    <div className="tags">
      {items.map((t) => (
        <span key={t} className="tag"><span className="tag-dot" />{t}</span>
      ))}
    </div>
  );
}

// ── Marquee ─────────────────────────────────────────────────
function Marquee({ items }) {
  const cluster = (
    <span>
      {items.map((it, i) => (
        <React.Fragment key={i}>
          <span>{it}</span>
          <span className="dot" />
        </React.Fragment>
      ))}
    </span>
  );
  return (
    <div className="marquee" aria-hidden="true">
      <div className="marquee-track">
        {cluster}{cluster}{cluster}{cluster}
      </div>
    </div>
  );
}

// ── Custom cursor (dot + ring + interactive states) ─────────
function Cursor() {
  useEffect(() => {
    if (matchMedia("(hover: none)").matches) return;
    document.body.classList.add("has-custom-cursor");
    const dot = document.createElement("div");
    const ring = document.createElement("div");
    dot.className = "cursor-dot";
    ring.className = "cursor-ring";
    document.body.appendChild(dot);
    document.body.appendChild(ring);

    let mx = -100, my = -100;       // target (mouse)
    let rx = -100, ry = -100;       // ring (eased)
    let raf = 0;

    const move = (e) => {
      mx = e.clientX; my = e.clientY;
      dot.style.transform = `translate3d(${mx}px,${my}px,0)`;
      if (!raf) raf = requestAnimationFrame(tick);
    };
    const tick = () => {
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      ring.style.transform = `translate3d(${rx}px,${ry}px,0) translate(-50%,-50%)`;
      if (Math.abs(mx - rx) > 0.1 || Math.abs(my - ry) > 0.1) {
        raf = requestAnimationFrame(tick);
      } else { raf = 0; }
    };

    const interactiveSel = "a, button, [role='button'], input, textarea, .proj-card, .img-frame, .theme-toggle, [data-cursor='hover']";
    const onOver = (e) => {
      const hit = e.target.closest?.(interactiveSel);
      if (hit) {
        document.body.classList.add("cur-hover");
        if (hit.matches("a, button, [role='button'], .theme-toggle")) {
          document.body.classList.add("cur-tap");
        }
        if (hit.matches(".proj-card, .img-frame, [data-cursor='view']")) {
          document.body.classList.add("cur-view");
        }
      }
    };
    const onOut = (e) => {
      const hit = e.target.closest?.(interactiveSel);
      if (hit && !hit.contains(e.relatedTarget)) {
        document.body.classList.remove("cur-hover", "cur-tap", "cur-view");
      }
    };
    const onDown = () => document.body.classList.add("cur-down");
    const onUp = () => document.body.classList.remove("cur-down");
    const onLeave = () => { dot.style.opacity = 0; ring.style.opacity = 0; };
    const onEnter = () => { dot.style.opacity = 1; ring.style.opacity = 1; };

    window.addEventListener("mousemove", move, { passive: true });
    document.addEventListener("mouseover", onOver);
    document.addEventListener("mouseout", onOut);
    window.addEventListener("mousedown", onDown);
    window.addEventListener("mouseup", onUp);
    document.addEventListener("mouseleave", onLeave);
    document.addEventListener("mouseenter", onEnter);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", move);
      document.removeEventListener("mouseover", onOver);
      document.removeEventListener("mouseout", onOut);
      window.removeEventListener("mousedown", onDown);
      window.removeEventListener("mouseup", onUp);
      document.removeEventListener("mouseleave", onLeave);
      document.removeEventListener("mouseenter", onEnter);
      dot.remove(); ring.remove();
      document.body.classList.remove("has-custom-cursor", "cur-hover", "cur-tap", "cur-view", "cur-down");
    };
  }, []);
  return null;
}

Object.assign(window, { Nav, ImgFrame, Tags, Marquee, ThemeToggle, Cursor, fmtTime });
