// shared.jsx — utilities, tiny components shared across pages

const { useEffect, useState, useRef, useCallback } = React;

// simple hash-based router
function useHashRoute() {
  const get = () => {
    const h = (window.location.hash || '#/').replace(/^#/, '');
    return h || '/';
  };
  const [route, setRoute] = useState(get());
  useEffect(() => {
    const onChange = () => {
      setRoute(get());
      window.scrollTo({ top: 0, behavior: 'instant' in window ? 'auto' : 'auto' });
    };
    window.addEventListener('hashchange', onChange);
    return () => window.removeEventListener('hashchange', onChange);
  }, []);
  return route;
}

function navigate(to) {
  window.location.hash = '#' + to;
}

// reveal-on-scroll — singleton observer, mutation-observed, idempotent.
// Keeps working across React re-renders that recreate DOM nodes.
let _revealIO = null;
const _revealSeen = new WeakSet();
function _ensureRevealObserver() {
  if (_revealIO) return _revealIO;
  if (!('IntersectionObserver' in window)) {
    _revealIO = { observe: (el) => el.classList.add('in'), disconnect: () => {} };
    return _revealIO;
  }
  _revealIO = new IntersectionObserver((entries) => {
    entries.forEach((e) => {
      if (e.isIntersecting) {
        e.target.classList.add('in');
        _revealIO.unobserve(e.target);
      }
    });
  }, { threshold: 0.05, rootMargin: '0px 0px -40px 0px' });
  return _revealIO;
}
function _scanReveal() {
  const io = _ensureRevealObserver();
  document.querySelectorAll('.reveal').forEach((el) => {
    if (_revealSeen.has(el)) return;
    _revealSeen.add(el);
    // if already above viewport (top area on load), trigger immediately
    const r = el.getBoundingClientRect();
    if (r.top < window.innerHeight && r.bottom > 0) {
      el.classList.add('in');
    } else {
      io.observe(el);
    }
  });
}
let _revealMO = null;
function useReveal() {
  useEffect(() => {
    _scanReveal();
    if (!_revealMO) {
      _revealMO = new MutationObserver(() => _scanReveal());
      _revealMO.observe(document.body, { childList: true, subtree: true });
    }
  });
}

// cursor halo
function useCursorHalo() {
  useEffect(() => {
    const halo = document.getElementById('halo');
    if (!halo) return;
    if (window.matchMedia && window.matchMedia('(hover: none)').matches) return;
    let raf = null;
    let x = window.innerWidth / 2, y = window.innerHeight / 2;
    let tx = x, ty = y;
    const onMove = (e) => {
      tx = e.clientX; ty = e.clientY;
      document.body.classList.add('has-halo');
      if (!raf) raf = requestAnimationFrame(tick);
    };
    const onLeave = () => { document.body.classList.remove('has-halo'); };
    const tick = () => {
      x += (tx - x) * 0.18;
      y += (ty - y) * 0.18;
      halo.style.transform = `translate(${x}px, ${y}px) translate(-50%,-50%)`;
      if (Math.abs(tx - x) > 0.2 || Math.abs(ty - y) > 0.2) {
        raf = requestAnimationFrame(tick);
      } else { raf = null; }
    };
    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseleave', onLeave);
    return () => {
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseleave', onLeave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);
}

// live clock (mono tabular)
function useClock() {
  const [now, setNow] = useState(() => new Date());
  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(t);
  }, []);
  return now;
}

// isolated leaf — only THIS component re-renders every second
function LiveClock() {
  const now = useClock();
  const timeStr = now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'Asia/Taipei' });
  return (
    <span className="mono tabular" style={{ color: 'var(--muted)', fontSize: 11, letterSpacing: '0.08em' }}>TPE · {timeStr}</span>
  );
}

function TopBar({ route }) {
  return (
    <header className="topbar">
      <div className="shell topbar-inner">
        <a href="#/" className="brand">
          <span className="dot" />
          <span>Walush</span>
          <em>— personal</em>
        </a>
        <nav className="topnav">
          <a href="#/" className={route === '/' ? 'active' : ''}>Index</a>
          <a href="#/zic-hat" className={route === '/zic-hat' ? 'active' : ''}>Zic Hat</a>
          <a href="#/motion" className={route === '/motion' ? 'active' : ''}>Motion PC</a>
          <LiveClock />
        </nav>
      </div>
    </header>
  );
}

// page container with reveal wiring
function Page({ children }) {
  useReveal();
  return <main className="shell">{children}</main>;
}

Object.assign(window, {
  useHashRoute, navigate, useReveal, useCursorHalo, useClock, TopBar, Page,
});
