/* global React, BIBLE_BOOKS, BIBLE_TEXT, VOTD_POOL, LANGUAGES, TranslationLoader */
const { useState, useEffect, useMemo, useRef, useCallback } = React;

/* ---------- routing (History API) ----------
 * Routes:
 *   /                              → home
 *   /read/<slug>/<book>/<chapter>  → reader for a specific translation
 *   /read/<book>/<chapter>         → legacy reader (uses active translation)
 *   /apps                          → app catalog
 *   /apps/<slug>                   → app detail
 *   /languages                     → translation-program status
 *   /downloads                     → downloads page
 *
 * Real path URLs (not hash). Each route is a real, crawlable URL so Google
 * and social-card scrapers see a distinct page per app and per translation.
 *
 * nginx serves index.html for any path that doesn't match a file
 *   (try_files $uri $uri/ /index.html;) — standard SPA fallback.
 *
 * Backward compat: if the URL still has a hash like #/apps/foo (old links,
 * Google's index, shared screenshots), we rewrite it to /apps/foo on boot
 * via history.replaceState. No 301 lost.
 *
 * The /read/ form is disambiguated by whether the segment after `read` is a
 * translation slug (always contains a hyphen) or a 3-letter book id.
 */
function parsePath() {
  // One-time: migrate any old hash URL to a clean path URL.
  if (window.location.hash && window.location.hash.startsWith('#/')) {
    const cleanPath = window.location.hash.slice(1) || '/';
    history.replaceState(null, '', cleanPath);
  }
  const path = window.location.pathname || '/';
  const parts = path.split('/').filter(Boolean);
  if (parts.length === 0) return { view: 'home' };
  if (parts[0] === 'read') {
    if (parts[1] && parts[1].includes('-')) {
      return {
        view: 'read',
        slug: parts[1],
        book: parts[2] || 'gen',
        chapter: parseInt(parts[3] || '1', 10),
      };
    }
    return {
      view: 'read',
      slug: null,
      book: parts[1] || 'gen',
      chapter: parseInt(parts[2] || '1', 10),
    };
  }
  if (parts[0] === 'languages') return { view: 'languages' };
  if (parts[0] === 'downloads') return { view: 'downloads' };
  if (parts[0] === 'apps') {
    return parts[1]
      ? { view: 'app-detail', slug: parts[1] }
      : { view: 'apps' };
  }
  return { view: 'home' };
}
// Backward-compat alias (some callers still use parseHash).
const parseHash = parsePath;
function useRoute() {
  const [route, setRoute] = useState(parsePath());
  useEffect(() => {
    const onChange = () => setRoute(parsePath());
    window.addEventListener('popstate', onChange);
    return () => window.removeEventListener('popstate', onChange);
  }, []);
  // navigate(path): pushes a new history entry and updates state.
  // Accepts '/foo' (preferred) and tolerates '#/foo' (legacy callers).
  const navigate = (target) => {
    let path = target || '/';
    if (path.startsWith('#')) path = path.slice(1);
    if (!path.startsWith('/')) path = '/' + path;
    if (path !== window.location.pathname + window.location.search) {
      history.pushState(null, '', path);
      setRoute(parsePath());
    }
  };
  return [route, navigate];
}

/* ---------- active translation hook ----------
 * Subscribes to TranslationLoader events. Returns:
 *   data        — { slug, books, text, meta } or null while booting
 *   switchTo    — async (slug) => void; updates URL hash and triggers fetch
 */
function useActiveTranslation() {
  const [data, setData] = useState(() => window.BIBLE_CURRENT || null);
  useEffect(() => {
    const handler = (e) => setData(e.detail.data);
    window.addEventListener('translationchanged', handler);
    return () => window.removeEventListener('translationchanged', handler);
  }, []);
  const switchTo = useCallback(async (slug) => {
    await window.TranslationLoader.setActive(slug);
  }, []);
  return [data, switchTo];
}

/* ---------- theme ---------- */
function useTheme() {
  const [theme, setTheme] = useState(() => {
    try { return localStorage.getItem('ob.theme') || 'light'; } catch { return 'light'; }
  });
  useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem('ob.theme', theme); } catch {}
  }, [theme]);
  return [theme, setTheme];
}

/* ---------- status bar ---------- */
function StatusBar({ theme, onToggleTheme }) {
  // True once the service worker is controlling this page — meaning the shell
  // is cached locally and a network drop won't break reading. Until then we
  // say "Offline-first" (architectural promise) rather than "Offline" (real-
  // time state).
  const [swReady, setSwReady] = useState(
    () => typeof navigator !== 'undefined' && !!navigator.serviceWorker
          && !!navigator.serviceWorker.controller
  );
  useEffect(() => {
    if (!('serviceWorker' in navigator)) return;
    const onCtrl = () => setSwReady(!!navigator.serviceWorker.controller);
    navigator.serviceWorker.addEventListener('controllerchange', onCtrl);
    // First load: SW may take a moment to claim. Poll briefly.
    let tries = 0;
    const t = setInterval(() => {
      if (navigator.serviceWorker.controller) { setSwReady(true); clearInterval(t); }
      else if (++tries > 20) clearInterval(t);
    }, 250);
    return () => {
      navigator.serviceWorker.removeEventListener('controllerchange', onCtrl);
      clearInterval(t);
    };
  }, []);

  return (
    <div className="status-bar">
      <div className="group">
        <span><span className="dot" /> {swReady ? 'Offline ready' : 'Offline-first'}</span>
        <span>No tracking</span>
        <span>No analytics</span>
        <span>No ads</span>
      </div>
      <div className="group">
        <span>Public domain</span>
        <button className="ghost" onClick={onToggleTheme}>
          {theme === 'light' ? 'Dark' : 'Light'}
        </button>
      </div>
    </div>
  );
}

/* ---------- header ---------- */
function Header({ route, navigate }) {
  const nav = (label, path, matchViews) => (
    <a
      href={path}
      className={matchViews.includes(route.view) ? 'active' : ''}
      onClick={(e) => { e.preventDefault(); navigate(path); }}
    >{label}</a>
  );
  const activeSlug = window.BIBLE_CURRENT && window.BIBLE_CURRENT.slug;
  const readHref = activeSlug ? `/read/${activeSlug}/gen/1` : '/read/gen/1';
  return (
    <header className="header">
      <a href="/" className="brand" onClick={(e) => { e.preventDefault(); navigate('/'); }}>
        <img className="brand-mark" src="/icons/mark.svg" alt="" aria-hidden="true" />
        <span className="brand-word">offline<span className="dot-char"></span>bible</span>
      </a>
      <nav className="nav">
        {nav('home', '/', ['home'])}
        {nav('read', readHref, ['read'])}
        {nav('apps', '/apps', ['apps', 'app-detail'])}
        {nav('languages', '/languages', ['languages'])}
        {nav('downloads', '/downloads', ['downloads'])}
      </nav>
    </header>
  );
}

/* ---------- footer ---------- */
function Footer() {
  return (
    <footer className="footer">
      <div>© 2026 · Free · No Rights Reserved</div>
      <div className="center">The Word, Offline, Forever</div>
      <div className="right">Public Domain · KJV 1769</div>
    </footer>
  );
}

Object.assign(window, { parseHash, useRoute, useTheme, useActiveTranslation, StatusBar, Header, Footer });
