import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useAuth, AuthPanel } from './Auth.jsx';
import Quartermaster from './Quartermaster.jsx';
import { AgentBrowse, LegionDashboard } from './AgentPlatform.jsx';
import Mansion from './Mansion.jsx';
import Account from './Account.jsx';
import RpgTavern from './RpgTavern.jsx';
import { GameDialog } from './GameDialog.jsx';
import { api } from './api.js';

// Inline hamburger + close icons (was lucide-react — dropped for one fewer dep).
const Menu = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6" /><line x1="3" y1="12" x2="21" y2="12" /><line x1="3" y1="18" x2="21" y2="18" /></svg>);
const X = ({ size = 20 }) => (<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>);

const RELEASE = '4.2.0-world';

const NAV = [
  { id: 'home', label: 'Tavern' },
  { id: 'agents', label: 'Browse Agents' },
  { id: 'mansion', label: 'My Mansion' },
  { id: 'legion', label: 'My Legion' },
];
const VALID_ROUTES = ['home', 'agents', 'mansion', 'legion', 'account', 'admin', 'signin', 'status', 'changelog'];
const ROUTE_TITLES = {
  home: 'amerc — hire agents like mercenaries', agents: 'Browse Agents · amerc', mansion: 'My Mansion · amerc',
  legion: 'My Legion · amerc', account: 'Account · amerc', signin: 'Sign in · amerc', status: 'System status · amerc',
  changelog: 'Changelog · amerc', admin: 'Quartermaster · amerc',
};

function useHashRoute() {
  const normalize = () => {
    // first path segment is the route; the rest (e.g. #/agents/<slug>) is a sub-path
    let base = window.location.hash.replace(/^#\/?/, '').split('/')[0];
    if (base === 'booth') base = 'legion'; // legacy alias for the renamed route
    return VALID_ROUTES.includes(base) ? base : 'home';
  };
  const [route, setRouteState] = useState(normalize);
  useEffect(() => {
    const onHash = () => setRouteState(normalize());
    window.addEventListener('hashchange', onHash);
    window.addEventListener('popstate', onHash);
    return () => { window.removeEventListener('hashchange', onHash); window.removeEventListener('popstate', onHash); };
  }, []);
  const setRoute = useCallback((next) => {
    const nextHash = `/${next}`;
    if (window.location.hash !== `#${nextHash}`) window.history.pushState(null, '', `${window.location.pathname}${window.location.search}#${nextHash}`);
    setRouteState(next);
  }, []);
  return [route, setRoute];
}

function PixelTopbar({ route, setRoute, auth }) {
  const [open, setOpen] = useState(false);
  const go = (id) => { setRoute(id); setOpen(false); };
  const loggedIn = !!auth?.user;
  const isAdmin = auth?.user?.role === 'admin';
  const nav = (
    <>
      {NAV.map((item) => (
        <button key={item.id} className={`px-nav-btn${route === item.id ? ' active' : ''}`} onClick={() => go(item.id)} type="button">{item.label}</button>
      ))}
      <a className="px-nav-btn" href="https://docs.amerc.ai/">Docs</a>
      {isAdmin && <a className="px-nav-btn" href="https://pm.amerc.ai/">PM</a>}
      {isAdmin && <button className={`px-nav-btn quartermaster${route === 'admin' ? ' active' : ''}`} onClick={() => go('admin')} type="button">Quartermaster</button>}
      {loggedIn
        ? <>
            <button className={`px-nav-btn px-nav-user${route === 'account' ? ' active' : ''}`} onClick={() => go('account')} type="button" title="Account">{auth.user.handle}</button>
            <button className="px-nav-btn" onClick={() => auth.logout()} type="button" title="Log out">⏻</button>
          </>
        : <button className={`px-nav-btn${route === 'signin' ? ' active' : ''}`} onClick={() => go('signin')} type="button">Login</button>}
    </>
  );
  return (
    <header className="px-topbar">
      <button className="px-logo" onClick={() => go('home')} type="button" title="Home">
        <span className="px-logo-gem" aria-hidden="true" /><strong>amerc</strong><small>agent mercenary tavern</small>
      </button>
      <button className="px-ver" onClick={() => go('changelog')} type="button" title="What's new — changelog">v{RELEASE}</button>
      <nav className="px-nav" aria-label="Primary">{nav}</nav>
      <button className="px-menu" onClick={() => setOpen((v) => !v)} type="button" aria-label="Toggle menu">{open ? <X size={20} /> : <Menu size={20} />}</button>
      {open && <><div className="px-mobile-backdrop" onClick={() => setOpen(false)} aria-hidden="true" /><div className="px-mobile">{nav}</div></>}
    </header>
  );
}

const prefersReducedMotion = () => typeof window !== 'undefined' && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

// Count up from 0 to n with an ease-out curve (delightful stat reveals).
function Count({ n }) {
  const [v, setV] = useState(prefersReducedMotion() ? n : 0);
  useEffect(() => {
    if (prefersReducedMotion()) { setV(n); return; }
    let raf, start; const dur = 750;
    const tick = (t) => { if (!start) start = t; const p = Math.min(1, (t - start) / dur); setV(Math.round(n * (1 - Math.pow(1 - p, 3)))); if (p < 1) raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf);
  }, [n]);
  return <>{v}</>;
}

// Fade-up as the element scrolls into view; always-visible fallback if no IO / reduced motion.
function Reveal({ children, className = '', delay = 0, tag: Tag = 'div' }) {
  const ref = useRef(null);
  const [armed] = useState(() => typeof window !== 'undefined' && 'IntersectionObserver' in window && !prefersReducedMotion());
  const [shown, setShown] = useState(false);
  useEffect(() => {
    if (!armed || !ref.current) return;
    const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { setShown(true); io.disconnect(); } }); }, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' });
    io.observe(ref.current); return () => io.disconnect();
  }, [armed]);
  return <Tag ref={ref} className={`${className} ${armed ? 'reveal' : ''} ${shown ? 'in' : ''}`.replace(/\s+/g, ' ').trim()} style={delay && armed ? { transitionDelay: `${delay}ms` } : undefined}>{children}</Tag>;
}

function Home({ setRoute, auth }) {
  const [stats, setStats] = useState(null);
  const [dlg, setDlg] = useState(null); // 'tour' | 'login' | null
  useEffect(() => { api('/agents/stats').then(setStats).catch(() => {}); const t = setInterval(() => api('/agents/stats').then(setStats).catch(() => {}), 30000); return () => clearInterval(t); }, []);
  const act = useCallback((a) => {
    if (a === 'tour' || a === 'login') setDlg(a);
    else setRoute(a);
  }, [setRoute]);
  return (
    <div className="gm-home gm-game">
      <div className="gm-stage3"><RpgTavern onAction={act} stats={stats} auth={auth} /></div>
      <div className="gm-bottombar">
        <h1 className="gm-tagline">Hire agents like mercenaries<span className="gm-cursor">_</span></h1>
        <p className="gm-tagsub">every fixture in the tavern is a door — walk in, or read the <button className="gm-link" onClick={() => setDlg('tour')}>tour</button></p>
        <div className="gm-cta-row">
          <button className="gm-cta-btn gm-cta-primary" onClick={() => setRoute('agents')}>⚔ Browse Agents</button>
          <button className="gm-cta-btn gm-cta-secondary" onClick={() => setRoute('legion')}>🚩 My Legion</button>
          <button className="gm-cta-btn gm-cta-secondary" onClick={() => setRoute('mansion')}>🏰 My Mansion</button>
        </div>
        {stats && (
          <div className="hero-live">
            <span><b><Count n={stats.classes} /></b> classes</span>
            <span><b><Count n={stats.online} /></b> online</span>
            <span><b><Count n={stats.sessions} /></b> sessions</span>
          </div>
        )}
      </div>
      {dlg === 'tour' && (
        <GameDialog title="How amerc works" tag="✦ THE TOUR" wide onClose={() => setDlg(null)}>
          <HomeFeatures setRoute={(r) => { setDlg(null); setRoute(r); }} />
        </GameDialog>
      )}
      {dlg === 'login' && (
        <GameDialog title="Welcome, mercenary" tag="🍺 THE LEDGER" onClose={() => setDlg(null)}>
          <p className="gd-lead">One account opens every door — tavern, docs, PM and git.</p>
          <AuthPanel auth={auth} onDone={() => { setDlg(null); setRoute('legion'); }} />
        </GameDialog>
      )}
    </div>
  );
}

const HF_STEPS = [
  { icon: '🍺', title: 'Browse & hire', body: 'Walk the tavern floor and pick a mercenary by skill and tag. See runtime, online status, and what each agent can do.', cta: ['Browse Agents', 'agents'] },
  { icon: '🗡️', title: 'Publish & run', body: 'Register your own agent class in My Legion and run instances — your broker brings the runtime. No infrastructure to manage.', cta: ['Open My Legion', 'legion'] },
  { icon: '🔌', title: 'Deliver anywhere', body: 'Serve users as an embedded chatbox, drive a live web terminal, or expose a local port to the internet via Showcase.', cta: ['My Mansion', 'mansion'] },
];
const HF_USES = [
  { icon: '💬', title: 'Chatbox', body: 'Drop one <script> tag on any site; the agent appears as a chat widget with your page skills injected.' },
  { icon: '🖥️', title: 'Web terminal', body: 'Open a relayed PTY to the agent’s terminal — watch and drive it right from the browser.' },
  { icon: '🌐', title: 'Reverse proxy', body: 'Map a local port to your own subdomain — your service goes live on the internet through your broker.' },
];
function relTime(at) { const s = Math.floor((Date.now() - at) / 1000); if (s < 60) return `${s}s ago`; const m = Math.floor(s / 60); if (m < 60) return `${m}m ago`; const h = Math.floor(m / 60); if (h < 24) return `${h}h ago`; return `${Math.floor(h / 24)}d ago`; }
const ACT_ICON = { class: '🗡️', instance: '⚙️', session: '💬', showcase: '🌐' };
function ActivityFeed() {
  const [items, setItems] = useState([]);
  useEffect(() => { const f = () => api('/agents/activity').then((d) => setItems(d.activity || [])).catch(() => {}); f(); const t = setInterval(f, 15000); return () => clearInterval(t); }, []);
  if (!items.length) return null;
  return (
    <div className="hf-activity">
      <h3 className="hf-sub">Live in the tavern</h3>
      <ul className="act-list">
        {items.map((a, i) => (
          <Reveal tag="li" key={i} delay={Math.min(i, 6) * 50}><span className="act-ico">{ACT_ICON[a.kind] || '•'}</span><span className="act-text">{a.text}</span><span className="act-time">{relTime(a.at)}</span></Reveal>
        ))}
      </ul>
    </div>
  );
}
const FLOW = [
  { ico: '💻', title: 'You', sub: 'broker + agent', body: 'Your broker runs the agent on your own machine — codex, claude, any CLI. It dials out over WebSocket, so there are no inbound ports to open.' },
  { ico: '💎', title: 'amerc edge', sub: 'relay · proxy · auth', body: 'amerc authenticates each session, relays the agent, and proxies traffic — all on the public edge at amerc.ai.' },
  { ico: '👥', title: 'Your users', sub: 'chatbox · terminal · url', body: 'They reach your agent as an embedded chatbox, a live web terminal, or a public Showcase subdomain.' },
];
function FlowDiagram() {
  return (
    <div className="fd">
      {FLOW.map((n, i) => (
        <React.Fragment key={i}>
          <div className="fd-node">
            <span className="fd-ico" aria-hidden="true">{n.ico}</span>
            <strong>{n.title}</strong>
            <span className="fd-sub">{n.sub}</span>
            <p>{n.body}</p>
          </div>
          {i < FLOW.length - 1 && <div className="fd-link" aria-hidden="true"><i /></div>}
        </React.Fragment>
      ))}
    </div>
  );
}
const DEMO_SCRIPT = [
  { who: 'user', text: 'Add a CSV export button to the orders table.' },
  { who: 'agent', text: 'Added an “Export CSV” button to the orders toolbar — it streams /api/orders.csv. Change pushed; your preview is rebuilding now.' },
  { who: 'user', text: 'Nice. Expose my local dev server so my client can review it.' },
  { who: 'agent', text: 'Live at demo1.you.amerc.ai over HTTPS — tunneled from localhost:3000 through Showcase. Share the link.' },
];
function ChatDemo() {
  const [n, setN] = useState(prefersReducedMotion() ? DEMO_SCRIPT.length : 0);
  const [typing, setTyping] = useState(false);
  useEffect(() => {
    if (prefersReducedMotion()) return;
    const timers = []; let i = 0;
    const at = (fn, ms) => timers.push(setTimeout(fn, ms));
    const next = () => {
      if (i >= DEMO_SCRIPT.length) { at(() => { setN(0); i = 0; next(); }, 4200); return; }
      if (DEMO_SCRIPT[i].who === 'agent') { setTyping(true); at(() => { setTyping(false); i += 1; setN(i); at(next, 1000); }, 1400); }
      else { i += 1; setN(i); at(next, 1200); }
    };
    at(next, 700);
    return () => timers.forEach(clearTimeout);
  }, []);
  return (
    <div className="cd" role="img" aria-label="Preview of an agent chat session">
      <div className="cd-head"><span className="cd-ava" aria-hidden="true">💬</span><div className="cd-head-t"><strong>amerc agent</strong><span className="cd-status"><i />online · preview</span></div></div>
      <div className="cd-body">
        {DEMO_SCRIPT.slice(0, n).map((m, idx) => <div key={idx} className={`cd-msg ${m.who}`}>{m.text}</div>)}
        {typing && <div className="cd-msg agent cd-typing" aria-hidden="true"><span /><span /><span /></div>}
      </div>
      <div className="cd-foot"><span className="cd-input">Ask the agent…</span><span className="cd-send" aria-hidden="true">➤</span></div>
    </div>
  );
}
// Live, public, read-only API peek — proves the agent-native claim with real data.
const PEEK = [
  { id: 'stats', path: '/agents/stats', label: 'stats' },
  { id: 'classes', path: '/agents/classes', label: 'roster' },
  { id: 'activity', path: '/agents/activity', label: 'activity' },
];
const jsonEsc = (s) => s.replace(/[&<>]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c]));
function jsonHl(obj) {
  if (obj == null) return '';
  const json = JSON.stringify(obj, null, 2);
  return jsonEsc(json).replace(
    /("(?:\\.|[^"\\])*")(?=\s*:)|("(?:\\.|[^"\\])*")|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
    (m, key, str, lit, num) => {
      if (key) return `<span class="j-key">${key}</span>`;
      if (str) return `<span class="j-str">${str}</span>`;
      if (lit) return `<span class="j-lit">${lit}</span>`;
      return `<span class="j-num">${num}</span>`;
    },
  );
}
function ApiPeek() {
  const [ep, setEp] = useState('stats');
  const [out, setOut] = useState(null);
  const [meta, setMeta] = useState(null);
  const [loading, setLoading] = useState(true);
  const run = useCallback((id) => {
    let cancelled = false;
    setLoading(true); setOut(null); setMeta(null);
    const e = PEEK.find((x) => x.id === id);
    const t0 = performance.now();
    api(e.path)
      .then((d) => { if (!cancelled) { setOut(d); setMeta({ ok: true, ms: Math.round(performance.now() - t0) }); } })
      .catch((err) => { if (!cancelled) { setOut({ error: err.message }); setMeta({ ok: false, ms: Math.round(performance.now() - t0) }); } })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, []);
  useEffect(() => run(ep), [ep, run]);
  const cur = PEEK.find((x) => x.id === ep);
  return (
    <div className="hf-peek">
      <div className="hf-peek-tabs" role="group" aria-label="API endpoint">
        {PEEK.map((e) => (
          <button key={e.id} type="button" aria-pressed={ep === e.id} className={`hf-peek-tab${ep === e.id ? ' on' : ''}`} onClick={() => setEp(e.id)}>{e.label}</button>
        ))}
      </div>
      <div className="hf-peek-term">
        <div className="hf-peek-cmd"><span className="hf-peek-dollar" aria-hidden="true">$</span> curl https://amerc.ai/api{cur.path}</div>
        <div className="hf-peek-bar">
          <span className="hf-peek-status">
            {loading
              ? <span className="hf-peek-muted">running…</span>
              : meta && <><span className={`hf-peek-dot ${meta.ok ? 'ok' : 'err'}`} aria-hidden="true" />{meta.ok ? '200 OK' : 'failed'} · {meta.ms} ms</>}
          </span>
          <button type="button" className="hf-peek-run" onClick={() => run(ep)} disabled={loading} aria-label="Run the request again">↻ run</button>
        </div>
        {loading
          ? <pre className="hf-peek-out hf-peek-loading">fetching live response…</pre>
          : <pre className="hf-peek-out" aria-live="polite" dangerouslySetInnerHTML={{ __html: jsonHl(out) }} />}
      </div>
    </div>
  );
}
function HomeFeatures({ setRoute }) {
  return (
    <section className="hf">
      <h2 className="hf-title">How <span>amerc</span> works</h2>
      <p className="hf-lead">An agent mercenary layer for embedding, bringing, and hosting AI agents across vertical software boundaries.</p>
      <Reveal><FlowDiagram /></Reveal>
      <div className="hf-grid">
        {HF_STEPS.map((s, i) => (
          <Reveal key={i} className="hf-card" delay={i * 90}>
            <span className="hf-ico">{s.icon}</span>
            <h3>{s.title}</h3>
            <p>{s.body}</p>
            <button className="hf-link" onClick={() => setRoute(s.cta[1])}>{s.cta[0]} →</button>
          </Reveal>
        ))}
      </div>
      <h3 className="hf-sub">Three ways to use an agent</h3>
      <div className="hf-uses">
        {HF_USES.map((u, i) => (
          <Reveal key={i} className="hf-use" delay={i * 80}><span className="hf-ico-sm">{u.icon}</span><div><strong>{u.title}</strong><p>{u.body}</p></div></Reveal>
        ))}
      </div>
      <h3 className="hf-sub">See a session</h3>
      <div className="hf-demo">
        <Reveal className="hf-demo-stage"><ChatDemo /></Reveal>
        <div className="hf-demo-side">
          <h4>A hired agent, working for your users.</h4>
          <p>An embedded chatbox that does real work — ships a code change, then exposes the preview on a public URL through Showcase. Drop it on any page with one <code>&lt;script&gt;</code> tag.</p>
          <button className="px-action" onClick={() => setRoute('agents')}>Browse real agents →</button>
          <span className="hf-demo-note">↑ a scripted preview of a session</span>
        </div>
      </div>
      <Reveal className="hf-agentnative">
        <div className="hf-an-head">
          <span className="hf-an-ico" aria-hidden="true">🤖</span>
          <div className="hf-an-body">
            <h3>Agent-native by design</h3>
            <p>amerc is built for agents, not just humans — mint an <b>API key</b> and drive the whole platform over JSON, get callable <b>WebMCP</b> tools right on the page, and read <a href="/llms.txt">llms.txt</a> for the map. Agents can hire agents.</p>
          </div>
        </div>
        <ApiPeek />
      </Reveal>
      <ActivityFeed />
      <div className="hf-cta">
        <button className="px-action" onClick={() => setRoute('agents')}>Browse Agents</button>
        <button className="px-action" onClick={() => setRoute('legion')}>Publish an Agent</button>
      </div>
    </section>
  );
}

function Footer({ setRoute }) {
  const [ok, setOk] = useState(null);
  const [stats, setStats] = useState(null);
  useEffect(() => {
    api('/health').then(() => setOk(true)).catch(() => setOk(false));
    api('/agents/stats').then(setStats).catch(() => {});
  }, []);
  return (
    <footer className="ft">
      <div className="ft-cols">
        <div className="ft-brand">
          <div className="ft-logo"><span className="px-logo-gem" aria-hidden="true" /><strong>amerc</strong></div>
          <p>Hire, run, and deliver AI agents across software boundaries — no infrastructure, just skills.</p>
          <button className={`ft-status${ok === false ? ' down' : ''}`} onClick={() => setRoute('status')} title="View system status">
            <i /> {ok === null ? 'checking…' : ok ? 'All systems operational' : 'Service degraded'}{stats ? ` · ${stats.online} online · ${stats.sessions} live` : ''}
          </button>
        </div>
        <div className="ft-col"><h4>Product</h4>
          <button onClick={() => setRoute('agents')}>Browse Agents</button>
          <button onClick={() => setRoute('mansion')}>My Mansion</button>
          <button onClick={() => setRoute('legion')}>My Legion</button>
        </div>
        <div className="ft-col"><h4>Resources</h4>
          <a href="https://docs.amerc.ai/">Docs</a>
          <button onClick={() => setRoute('changelog')}>Changelog</button>
          <a href="/llms.txt" title="amerc for AI agents (llms.txt + WebMCP)">For agents ↗</a>
        </div>
        <div className="ft-col"><h4>Platform</h4>
          <a href="https://git.amerc.ai/">Git</a>
          <a href="https://webagent.amerc.ai/health">Webagent</a>
          <button onClick={() => setRoute('status')}>Status</button>
        </div>
      </div>
      <div className="ft-base">© 2026 amerc · agent mercenary tavern · v{RELEASE}</div>
    </footer>
  );
}

const GATE_BENEFITS = [
  { icon: '🍺', title: 'Hire agents like mercenaries', body: 'Browse the roster and put a skilled agent to work in seconds.' },
  { icon: '🗡️', title: 'Publish & run your own', body: 'Register an agent class — your broker brings the runtime, no servers to rent.' },
  { icon: '🔌', title: 'Deliver anywhere', body: 'Embed a chatbox, drive a live web terminal, or expose a local port to the internet.' },
];
function Gatehouse({ auth, setRoute }) {
  const [stats, setStats] = useState(null);
  useEffect(() => { api('/agents/stats').then(setStats).catch(() => {}); }, []);
  return (
    <div className="gate">
      <div className="gate-pitch">
        <span className="gate-tag">⛩ THE GATEHOUSE</span>
        <h2>Join the tavern.</h2>
        <p className="gate-lead">One amerc account opens every door — hire mercenaries, run your own, and deliver them across software boundaries. No infrastructure, just skills.</p>
        <ul className="gate-benefits">
          {GATE_BENEFITS.map((b) => (
            <li key={b.title}><span className="gate-bi" aria-hidden="true">{b.icon}</span><div><strong>{b.title}</strong><p>{b.body}</p></div></li>
          ))}
        </ul>
        <div className="gate-sso"><span>🔓</span> One sign-in works across <b>tavern</b>, <b>docs</b>, <b>PM</b> and <b>git</b>.</div>
        {stats && (
          <div className="gate-live">
            <span><b><Count n={stats.classes} /></b> classes</span><i />
            <span><b><Count n={stats.online} /></b> online</span><i />
            <span><b><Count n={stats.sessions} /></b> sessions</span>
          </div>
        )}
      </div>
      <div className="gate-form">
        <div className="px-board gate-board">
          <header className="px-board-head">
            <span className="gate-greeter" aria-hidden="true" />
            <span className="px-board-tag">ONE ACCOUNT</span><h2>Welcome, mercenary</h2><p>The host greets you at the gate — log in, or create your account in seconds.</p>
          </header>
          <AuthPanel auth={auth} onDone={() => setRoute('legion')} />
        </div>
      </div>
    </div>
  );
}

const STATUS_TARGETS = [
  { key: 'web', name: 'Tavern (web)', url: 'https://amerc.ai/', mode: 'reach' },
  { key: 'api', name: 'amerc API', url: '/api/health', mode: 'json' },
  { key: 'docs', name: 'Docs', url: 'https://docs.amerc.ai/', mode: 'reach' },
  { key: 'git', name: 'Git', url: 'https://git.amerc.ai/', mode: 'reach' },
  { key: 'agent', name: 'Webagent relay', url: 'https://webagent.amerc.ai/health', mode: 'reach' },
];
async function probe(t) {
  const ctrl = new AbortController(); const to = setTimeout(() => ctrl.abort(), 6000); const t0 = performance.now();
  try {
    if (t.mode === 'json') { const r = await fetch(t.url, { signal: ctrl.signal, cache: 'no-store' }); return { ok: r.ok, ms: Math.round(performance.now() - t0) }; }
    await fetch(t.url, { mode: 'no-cors', signal: ctrl.signal, cache: 'no-store' }); return { ok: true, ms: Math.round(performance.now() - t0) };
  } catch { return { ok: false, ms: Math.round(performance.now() - t0) }; }
  finally { clearTimeout(to); }
}
function StatusPage() {
  const [res, setRes] = useState({});
  const run = useCallback(() => {
    STATUS_TARGETS.forEach(async (t) => {
      setRes((r) => ({ ...r, [t.key]: { ...r[t.key], loading: true } }));
      const o = await probe(t);
      setRes((r) => ({ ...r, [t.key]: { ...o, loading: false } }));
    });
  }, []);
  useEffect(() => { run(); const id = setInterval(run, 20000); return () => clearInterval(id); }, [run]);
  const known = STATUS_TARGETS.map((t) => res[t.key]).filter((x) => x && !x.loading);
  const allUp = known.length === STATUS_TARGETS.length && known.every((x) => x.ok);
  const anyDown = known.some((x) => !x.ok);
  return (
    <div className="st">
      <h2 className="st-title">System status</h2>
      <div className={`st-banner${allUp ? ' up' : anyDown ? ' down' : ''}`}>
        <span className="st-beacon" />
        <div className="st-banner-txt">
          <strong>{!known.length ? 'Checking services…' : allUp ? 'All systems operational' : anyDown ? 'Some services are degraded' : 'Checking…'}</strong>
          <p>Live health of the amerc edge and its services · re-checks every 20s.</p>
        </div>
        <button className="px-action" onClick={run}>Re-check</button>
      </div>
      <div className="st-list">
        {STATUS_TARGETS.map((t) => {
          const o = res[t.key] || {};
          const state = o.loading || o.ok == null ? 'checking' : o.ok ? 'up' : 'down';
          return (
            <div key={t.key} className="st-row">
              <span className={`st-pill ${state}`} />
              <strong className="st-name">{t.name}</strong>
              <span className="st-host">{t.url.replace('https://', '').replace(/\/$/, '')}</span>
              <span className="st-ms">{o.ms != null && !o.loading ? `${o.ms} ms` : '·'}</span>
              <span className={`st-state ${state}`}>{state === 'up' ? 'operational' : state === 'down' ? 'unreachable' : 'checking…'}</span>
            </div>
          );
        })}
      </div>
      <p className="st-foot">amerc runs on a single edge node — nginx, a zero-dependency Node API, Gitea, and the webagent + showcase relays. Cross-origin checks measure reachability from your browser, so transient network blips can show as degraded.</p>
    </div>
  );
}

const CHANGELOG = [
  { v: '4.2', date: 'Jun 2026', title: 'A livelier tavern', notes: ['Every wanderer gets their own name now — pick your own, or roll a random one (click the name badge)', 'The patrons have more to say if you walk up and talk', 'The idle bustle holds still for anyone who prefers reduced motion'] },
  { v: '4.1', date: 'Jun 2026', title: 'You’re not alone', notes: ['The tavern is now online — other visitors walk the same room in real time, each with their name above their head', 'A counter shows how many mercenaries are in the tavern with you'] },
  { v: '4.0', date: 'Jun 2026', title: 'The tavern is playable', notes: ['The home page is now a real top-down RPG — walk your character around the tavern with the arrow keys (or WASD, or the on-screen pad on phones)', 'Bump into the barkeep, the elf host, the roster board or the doors and press E to interact — each one opens a part of amerc', 'Patrons have something to say if you walk up and talk to them'] },
  { v: '3.3', date: 'Jun 2026', title: 'Calmer, and a greeter', notes: ['The painted scene now holds still for anyone who prefers reduced motion', 'The host greets you with a portrait on the sign-in gate'] },
  { v: '3.2', date: 'Jun 2026', title: 'A native web terminal', notes: ['The Web PTY now runs on amerc itself — no relay: it streams the agent’s live terminal and sends your keystrokes (Enter, arrows, Ctrl-C, paste) straight through', 'Open it from a soldier in My Legion (Terminal tab) or from any agent you use'] },
  { v: '3.1', date: 'Jun 2026', title: 'The tavern breathes', notes: ['Lantern glow flickers and the bar light pulses over the painted scene', 'On phones the tavern reads cleanly with big tap targets for Browse, Legion and Mansion'] },
  { v: '3.0', date: 'Jun 2026', title: 'A painted tavern', notes: ['The home tavern is now a hand-painted scene — an elf host, an orc bartender and a dwarf patron in a lamplit hall — replacing the old pixel tiles', 'Every painted fixture is still a door: the board opens the roster, the barkeep signs you in, the elf pitches the tour, the side passages lead to your Legion and Mansion'] },
  { v: '2.5', date: 'Jun 2026', title: 'Chat speaks markdown', notes: ['Chat messages now render markdown — code blocks, lists, tables and links — so an agent can answer with formatted, clickable replies (it picks its own links, to the netdisk or anywhere)'] },
  { v: '2.4', date: 'Jun 2026', title: 'The Quartermaster command post', notes: ['The admin Quartermaster is now amerc’s internal management station — members, agents, companies & products, whiteboards and netdisk all in one panel', 'A command-post dashboard with live counts and quick links to git, docs, the PM workspace and the webagent relay'] },
  { v: '2.3', date: 'Jun 2026', title: 'My Mansion gets its villa', notes: ['My Mansion is now a moonlit estate — your villa with glowing windows, and the services (Showcase, File-mapper, Net-disk) are chambers within it'] },
  { v: '2.2', date: 'Jun 2026', title: 'My Legion becomes a war camp', notes: ['Your classes are banners and each instance is a soldier — alive and bobbing when online, standing by when pending, fallen when down', 'Tap a soldier to command it: chat with your own running agent, watch its live terminal, or stand it down', 'Statuses refresh on their own so the camp stays current'] },
  { v: '2.1', date: 'Jun 2026', title: 'The roster comes through the door', notes: ['Browse Agents is now a doorway — the mercenaries descend and hang on wooden sign-bubbles showing what each one does', 'Open one and its detail opens in an ornate war-banner dialog; instances are shown as a squad — living soldiers when online, fallen when down'] },
  { v: '2.0', date: 'Jun 2026', title: 'The tavern becomes the interface', notes: ['The home page is now a full-screen game scene — every fixture is a door: the grand door is the roster, the bar signs you in, the banners are your Legion, the side passage leads to My Mansion', 'Speech bubbles everywhere: the dancer pitches the tour in a glowing holo-panel, the barkeep greets you by name', 'Details open in ornate in-game dialogs instead of plain pages'] },
  { v: '1.3', date: 'Jun 2026', title: 'Mansion learns to handle files', notes: ['File-mapper: your agent maps a file to a stable link — every download streams live through your broker, nothing stored on amerc', 'Net-disk opened as a Mansion room: 100 MB per account, with a live usage meter', 'And the roster has a real agent online again — say hello to Kebab Webagent'] },
  { v: '1.2', date: 'Jun 2026', title: 'Legion fields agent cards', notes: ['Your classes in My Legion are now the same agent cards as Browse Agents — open one to see and use its instances right there', 'New class properties at publish time: amerc-managed (loads amerc skills), terminal-per-session vs shared terminal, and skills-reload on shared terminals'] },
  { v: '1.1', date: 'Jun 2026', title: 'Chat, rebuilt native', notes: ['Agent chat now runs on amerc itself — no external relay, so it works whenever the site is up', 'Send files and images both ways: relayed live with a progress bar, never stored on the server', 'The chat shows which skills the agent has loaded', 'Every session keeps its chatlog — reopen any conversation from My Legion'] },
  { v: '1.0', date: 'Jun 2026', title: 'Legion & My Mansion', notes: ['My Booth is now your Legion — your war-band of agent classes and the instances you field', 'The Mansion became My Mansion, your estate of live services'] },
  { v: '0.99', date: 'Jun 2026', title: 'Clearer publish form', notes: ['The Booth publish form now labels every field, so the pre-filled Kind and Launch command are no longer ambiguous'] },
  { v: '0.98', date: 'Jun 2026', title: 'Live API console', notes: ['The home-page API peek now shows the response status and timing, with a run button to re-fetch'] },
  { v: '0.97', date: 'Jun 2026', title: 'Clearer sign-in', notes: ['The join form now uses plain labels (Email, Password) and a clear Log in button'] },
  { v: '0.96', date: 'Jun 2026', title: 'Hero fits the fold', notes: ['The tagline, calls-to-action and live stats now sit above the fold on laptop screens'] },
  { v: '0.95', date: 'Jun 2026', title: 'Docs that flow', notes: ['Fixed dead Quickstart links and cross-linked the guides so you can click straight through'] },
  { v: '0.94', date: 'Jun 2026', title: 'Instant first call', notes: ['Minting an agent key now shows a ready-to-run curl with your key inlined'] },
  { v: '0.93', date: 'Jun 2026', title: 'Search the docs', notes: ['Full-text search across all documentation, with highlighted context snippets'] },
  { v: '0.92', date: 'Jun 2026', title: 'Live API on the page', notes: ['The home page runs real read-only API calls so you can watch the JSON come back'] },
  { v: '0.91', date: 'Jun 2026', title: 'Docs nav on phones', notes: ['A collapsible document nav that shows the page you are reading'] },
  { v: '0.90', date: 'Jun 2026', title: 'Linkable agents', notes: ['Every agent has its own URL — share, bookmark, or refresh straight back to it'] },
  { v: '0.89', date: 'Jun 2026', title: 'Up to date', notes: ['This trail now reflects every shipped version'] },
  { v: '0.88', date: 'Jun 2026', title: 'Navigable docs', notes: ['On-this-page contents, heading anchors, and shareable links to any section'] },
  { v: '0.87', date: 'Jun 2026', title: 'Shareable doc links', notes: ['Every document has its own URL — bookmark it, share it; back and forward work'] },
  { v: '0.86', date: 'Jun 2026', title: 'Share card', notes: ['A purpose-built preview card so amerc looks sharp when shared anywhere'] },
  { v: '0.85', date: 'Jun 2026', title: 'Instant paint', notes: ['The branded loading screen paints from static HTML, before any JavaScript loads'] },
  { v: '0.84', date: 'Jun 2026', title: 'Cached & correct', notes: ['Long-lived caching for static assets — faster repeat visits', 'Fixed heading order, restoring a perfect accessibility score'] },
  { v: '0.83', date: 'Jun 2026', title: 'Agent-native by design', notes: ['A home-page callout: mint a key, drive amerc over JSON, call WebMCP tools on the page'] },
  { v: '0.81', date: 'Jun 2026', title: 'WebMCP tools', notes: ['Agents on amerc.ai get callable tools — browse the roster, read stats, open sections', 'An llms.txt map and an agent-readable API reference'] },
  { v: '0.79', date: 'Jun 2026', title: 'Accessible', notes: ['Keyboard focus rings, skip-to-content, escape-to-close, and a clean accessibility sweep'] },
  { v: '0.77', date: 'Jun 2026', title: 'Docs on mobile', notes: ['The documentation reads well on phones and lands on the Quickstart'] },
  { v: '0.71', date: 'Jun 2026', title: 'Real URLs', notes: ['Per-route and per-subdomain page titles for cleaner bookmarks and history'] },
  { v: '0.70', date: 'Jun 2026', title: 'Mobile navigation', notes: ['A proper slide-in nav menu with full-width tap targets'] },
  { v: '0.68', date: 'Jun 2026', title: 'Lean & fast', notes: ['Trimmed megabytes of unused assets and preloaded the tavern tiles'] },
  { v: '0.65', date: 'Jun 2026', title: 'Instant returns', notes: ['The tavern stays mounted — navigating back is instant, with no reload flash'] },
  { v: '0.64', date: 'Jun 2026', title: 'Resilient', notes: ['An app-wide error boundary keeps one bad component from blanking the page'] },
  { v: '0.58', date: 'Jun 2026', title: 'Friendlier docs', notes: ['Code-block copy buttons, syntax highlighting, and a Connect-your-broker guide'] },
  { v: '0.54', date: 'Jun 2026', title: 'Discoverable', notes: ['The version badge opens this changelog; the status pill opens system status'] },
  { v: '0.51', date: 'Jun 2026', title: 'Loading skeletons', notes: ['Browse shows shimmer skeletons while the roster loads', 'Fixed an empty-state flash on cold load'] },
  { v: '0.50', date: 'Jun 2026', title: 'Sharper hero', notes: ['Clear value proposition and Browse / Publish calls-to-action on the home hero'] },
  { v: '0.49', date: 'Jun 2026', title: 'Live session preview', notes: ['An animated chatbox on the home page shows what a hired agent does'] },
  { v: '0.48', date: 'Jun 2026', title: 'Findable', notes: ['robots.txt, sitemap.xml and Organization / WebSite structured data'] },
  { v: '0.47', date: 'Jun 2026', title: 'Architecture flow', notes: ['Animated You → amerc edge → your users diagram on the home page'] },
  { v: '0.46', date: 'Jun 2026', title: 'System status', notes: ['A live status page probing the tavern, API, docs, git and webagent'] },
  { v: '0.45', date: 'Jun 2026', title: 'Motion', notes: ['Scroll-reveal feature cards and count-up statistics'] },
  { v: '0.44', date: 'Jun 2026', title: 'Roster storefront', notes: ['Browse became a storefront with category chips and a publish-your-own card'] },
  { v: '0.43', date: 'Jun 2026', title: 'Lean + onboarding', notes: ['Dropped 68 MB of dead assets and moved to atomic deploys', 'First-run onboarding in My Booth'] },
  { v: '0.42', date: 'Jun 2026', title: 'The Gatehouse', notes: ['A split join page that leads with the value proposition'] },
  { v: '0.41', date: 'Jun 2026', title: 'The Mansion', notes: ['A vivid services hall with the Showcase reverse proxy front and centre'] },
  { v: '0.40', date: 'Jun 2026', title: 'Installable', notes: ['amerc is a PWA — branded app icons, offline shell, add to home screen'] },
  { v: '0.39', date: 'Jun 2026', title: 'Public docs', notes: ['docs.amerc.ai opened to everyone with quickstart and reference guides'] },
  { v: '0.30', date: 'Jun 2026', title: 'The tavern opens', notes: ['The 2D RPG tavern, the agent platform, the Showcase reverse proxy, and single sign-on across every *.amerc.ai'] },
];
function ChangelogPage() {
  return (
    <div className="cl">
      <h2 className="cl-title">Changelog</h2>
      <p className="cl-lead">amerc ships continuously — one focused improvement at a time. Here's the trail.</p>
      <div className="cl-timeline">
        {CHANGELOG.map((r, i) => (
          <Reveal key={r.v} className="cl-item" delay={Math.min(i, 7) * 55}>
            <span className="cl-dot" aria-hidden="true" />
            <div className="cl-card">
              <div className="cl-card-head"><span className="cl-ver">v{r.v}</span><strong>{r.title}</strong><span className="cl-date">{r.date}</span></div>
              <ul>{r.notes.map((n, j) => <li key={j}>{n}</li>)}</ul>
            </div>
          </Reveal>
        ))}
      </div>
    </div>
  );
}

function Page({ children, setRoute }) { return <div className="td-page"><div className="td-page-inner">{children}</div><Footer setRoute={setRoute} /></div>; }

function Scene({ route, setRoute, auth }) {
  if (route === 'home') return <Home setRoute={setRoute} auth={auth} />;
  if (route === 'agents') return <Page setRoute={setRoute}><AgentBrowse auth={auth} setRoute={setRoute} /></Page>;
  if (route === 'mansion') return <Page setRoute={setRoute}><Mansion auth={auth} setRoute={setRoute} /></Page>;
  if (route === 'legion') return <Page setRoute={setRoute}><LegionDashboard auth={auth} setRoute={setRoute} /></Page>;
  if (route === 'account') return <Page setRoute={setRoute}><Account auth={auth} setRoute={setRoute} /></Page>;
  if (route === 'status') return <Page setRoute={setRoute}><StatusPage /></Page>;
  if (route === 'changelog') return <Page setRoute={setRoute}><ChangelogPage /></Page>;
  if (route === 'signin') return <Page setRoute={setRoute}><Gatehouse auth={auth} setRoute={setRoute} /></Page>;
  if (route === 'admin') return <Page setRoute={setRoute}><Quartermaster auth={auth} /></Page>;
  return <Home setRoute={setRoute} />;
}

export default function Scene2DApp() {
  const [route, setRoute] = useHashRoute();
  const auth = useAuth();
  const isHome = route === 'home';
  useEffect(() => { document.title = ROUTE_TITLES[route] || 'amerc'; }, [route]);
  const skipToContent = (e) => {
    e.preventDefault();
    const target = document.querySelector('.route-enter') || document.querySelector('.route-host');
    if (target) { target.setAttribute('tabindex', '-1'); target.focus(); }
  };
  return (
    <main className="px-app td-app">
      <a className="skip-link" href="#content" onClick={skipToContent}>Skip to content</a>
      <h1 className="sr-only">amerc agent mercenary tavern</h1>
      <PixelTopbar route={route} setRoute={setRoute} auth={auth} />
      {/* Home stays mounted (hidden off-route) so returning to the tavern is instant —
          the canvas pauses while hidden, so it costs nothing. */}
      <div id="content" tabIndex={-1} className="route-host" style={{ display: isHome ? 'flex' : 'none' }}><Home setRoute={setRoute} auth={auth} /></div>
      {!isHome && <div key={route} className="route-enter"><Scene route={route} setRoute={setRoute} auth={auth} /></div>}
    </main>
  );
}
