// App.jsx — root: auth gate, role + view routing, shell assembly, preferences.
(function () {
  const { useState, useEffect } = React;
  const D = window.JS_DATA;

  const PREF_KEY = 'jiffyscore.prefs.v1';
  const PREF_DEFAULTS = { name: '', brand: 'default', mode: 'dark', density: 'cozy', typeScale: 'regular', motion: 'full', scoreLab: 'forensic' };
  const loadPrefs = () => {
    try { return { ...PREF_DEFAULTS, ...JSON.parse(localStorage.getItem(PREF_KEY) || '{}') }; }
    catch (e) { return { ...PREF_DEFAULTS }; }
  };

  // ---- super-admin overview (platform hub) -------------------------------
  function AdminOverview({ go }) {
    return (
      <div className="js-view js-view--wide">
        <div className="js-pagehead"><div><p className="eyebrow">Platform · Jiffy Resources</p><h1>Platform <em>overview</em></h1>
          <p className="sub">Health across every tenant, the live job queue, and launch readiness — one console for running JiffyScore.</p></div>
          <div className="js-head-actions"><Btn kind="ghost" icon="rocket" sm onClick={() => go('launch')}>Launch</Btn><Btn icon="server" onClick={() => go('production')}>Operations</Btn></div></div>
        <div className="js-stats" style={{ marginBottom: 22 }}>{D.OPS.metrics.map(m => <Stat key={m.k} {...m} dir={m.dir} />)}</div>
        <div className="js-cols-main">
          <Panel>
            <PanelHead eyebrow="Live" title="Job queue" desc="Most recent crawls across all tenants."><span className="js-link-gold" onClick={() => go('production')}>Operations <Icon name="arrow" size={14} /></span></PanelHead>
            <div className="js-rows">
              {D.OPS.jobs.slice(0, 4).map(j => (
                <div key={j.id} className="js-rowcard" style={{ gridTemplateColumns: 'auto 1fr auto auto', cursor: 'default' }}>
                  <div style={{ width: 38, height: 38, borderRadius: 10, background: 'var(--surface-2)', display: 'grid', placeItems: 'center', color: 'var(--gold)', border: '1px solid var(--line-soft)' }}><Icon name="activity" size={17} /></div>
                  <div><div className="rc-title" style={{ fontSize: 14, fontFamily: 'var(--font-mono)' }}>{j.site}</div><div className="rc-sub">{j.id} · {j.pages} pages</div></div>
                  <span className="js-tag">{j.started}</span>
                  <Pill tone={j.state === 'done' ? 'green' : j.state === 'running' ? 'amber' : j.state === 'failed' ? 'coral' : null} dot={false}>{j.state}</Pill>
                </div>
              ))}
            </div>
          </Panel>
          <Panel raised className="js-artback" style={{ display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 24, alignItems: 'center' }}>
            <ScoreDial value={D.LAUNCH.readiness} label="Readiness" size="sm" />
            <div><p className="eyebrow">Launch</p><h3 style={{ fontFamily: 'var(--font-display)', fontSize: 22, margin: '10px 0 8px', color: 'var(--ink)' }}>Nearly there</h3>
              <p style={{ fontSize: 13.5, color: 'var(--ink-soft)', margin: '0 0 14px', lineHeight: 1.5 }}>12/13 QA suites green. Two items in progress.</p>
              <Btn sm kind="gold" icon="rocket" onClick={() => go('launch')}>Open readiness</Btn></div>
          </Panel>
        </div>
      </div>
    );
  }

  // ---- view router -------------------------------------------------------
  function View(props) {
    const { view, role } = props;
    if (!props.hasRun && !['home', 'run-audit', 'settings', 'integrations', 'production', 'launch', 'clients'].includes(view)) {
      return <NoAuditState go={props.go} />;
    }
    switch (view) {
      case 'home':
        if (role === 'client') return <ClientHome {...props} />;
        if (role === 'agency') return <AgencyDashboard {...props} />;
        return <AdminOverview {...props} />;
      case 'run-audit': return <RunAudit {...props} />;
      case 'results': return <ResultsReport {...props} />;
      case 'recommendations': return <Recommendations {...props} />;
      case 'exports': return <Exports {...props} />;
      case 'monitoring': return <Monitoring {...props} />;
      case 'answer': return <AnswerPreview {...props} />;
      case 'clients': return role === 'super_admin' ? <AdminTenants {...props} /> : <Clients {...props} />;
      case 'compare': return <Compare {...props} />;
      case 'visibility': return <Visibility {...props} />;
      case 'knowledge': return <Knowledge {...props} />;
      case 'integrations': return <Integrations {...props} />;
      case 'whitelabel': return <Whitelabel {...props} />;
      case 'production': return <Production {...props} />;
      case 'launch': return <Launch {...props} />;
      case 'settings': return <Settings {...props} />;
      default: return <ClientHome {...props} />;
    }
  }

  function NoAuditState({ go }) {
    return (
      <div className="js-view">
        <div className="js-pagehead">
          <div>
            <p className="eyebrow">No audit selected</p>
            <h1>Run your first <em>real audit</em></h1>
            <p className="sub">This workspace has no backend audit records yet. The old Claude fixture audit is no longer used as fallback data.</p>
          </div>
          <div className="js-head-actions"><Btn icon="play" onClick={() => go('run-audit')}>Run audit</Btn></div>
        </div>
        <Panel>
          <div className="js-gap sev-low" style={{ margin: 0 }}>
            <h4><Icon name="shieldCheck" size={16} style={{ color: 'var(--emerald)' }} />Backend-driven state</h4>
            <p style={{ margin: 0 }}>Results, recommendations, exports, comparison, visibility, and monitoring unlock after a real audit is created or selected.</p>
          </div>
        </Panel>
      </div>
    );
  }

  function App() {
    const [prefs, setPrefs] = useState(loadPrefs);
    const [prefsOpen, setPrefsOpen] = useState(false);
    const initialSession = window.JS_SESSION && window.JS_SESSION.getSession ? window.JS_SESSION.getSession() : null;
    const [session, setSession] = useState(initialSession);
    const [authed, setAuthed] = useState(Boolean(initialSession));
    const [role, setRole] = useState(initialSession ? window.JS_SESSION.sessionRole(initialSession) : 'agency');
    const [view, setView] = useState('home');
    const [collapsed, setCollapsed] = useState(false);
    const [drawer, setDrawer] = useState(false);
    const [hasRun, setHasRun] = useState(Boolean(D.AUDIT && D.AUDIT.id));
    const [packGenerated, setPackGenerated] = useState(Boolean(D.ASSETS && D.ASSETS.length));
    const [assets, setAssets] = useState(D.ASSETS);
    const [scoreFocus, setScoreFocus] = useState(null);   // null | 'total' | dimension key
    const [stealQuery, setStealQuery] = useState(null);   // null | a VISIBILITY query string
    const [tourRun, setTourRun] = useState(false);

    const openScore = () => alert('Score simulation is retired until backend score-impact projections are available. The displayed scores are loaded from the audit backend.');
    const closeScore = () => setScoreFocus(null);
    const openSteal = (q) => setStealQuery(q);
    const closeSteal = () => setStealQuery(null);
    // ship the simulated fixes: approve their assets, mark the pack generated,
    // and route the user to review — the simulator flows into the real pipeline.
    const onShip = async (fixIds) => {
      const fixes = (window.JS_DATA.FIXES || []).filter(f => fixIds.includes(f.id));
      const assetIds = fixes.map(f => f.asset).filter(Boolean);
      setAssets(as => as.map(a => assetIds.includes(a.id) ? { ...a, status: 'approved' } : a));
      if (D.AUDIT?.id && window.JS_API) {
        for (const assetId of assetIds) {
          try { await window.JS_API.audits.updateAsset(D.AUDIT.id, assetId, 'approved'); } catch (e) {}
        }
      }
      setPackGenerated(true);
      go('recommendations');
    };

    const setPref = (k, v) => setPrefs(p => { const next = { ...p, [k]: v }; try { localStorage.setItem(PREF_KEY, JSON.stringify(next)); } catch (e) {} return next; });
    const resetPrefs = () => { try { localStorage.removeItem(PREF_KEY); } catch (e) {} setPrefs({ ...PREF_DEFAULTS }); };

    useEffect(() => {
      if (!initialSession || !window.JS_LIVE_DATA) return;
      let cancelled = false;
      (async () => {
        try {
          const refreshed = await window.JS_API.me();
          const summary = await window.JS_LIVE_DATA.hydrate();
          if (cancelled) return;
          setSession(refreshed);
          setRole(window.JS_SESSION.sessionRole(refreshed));
          setAssets([...D.ASSETS]);
          setHasRun(Boolean(summary.audit?.id));
          setPackGenerated(Boolean(D.ASSETS && D.ASSETS.length));
        } catch (e) {
          if (!cancelled) logout();
        }
      })();
      return () => { cancelled = true; };
    }, []);

    // apply theme prefs to <html>
    useEffect(() => {
      const h = document.documentElement;
      h.setAttribute('data-brand', prefs.brand);
      h.setAttribute('data-mode', prefs.mode);
      h.setAttribute('data-density', prefs.density);
      h.setAttribute('data-type-scale', prefs.typeScale);
      h.setAttribute('data-motion', prefs.motion);
    }, [prefs.brand, prefs.mode, prefs.density, prefs.typeScale, prefs.motion]);

    // smooth cross-fade when appearance / palette flips
    useEffect(() => {
      const h = document.documentElement;
      h.classList.add('mode-anim');
      const t = setTimeout(() => h.classList.remove('mode-anim'), 480);
      return () => clearTimeout(t);
    }, [prefs.mode, prefs.brand]);

    // host protocol: let the toolbar "Tweaks" toggle open Preferences too
    useEffect(() => {
      const onMsg = (e) => {
        const t = e && e.data && e.data.type;
        if (t === '__activate_edit_mode') setPrefsOpen(true);
        else if (t === '__deactivate_edit_mode') setPrefsOpen(false);
      };
      window.addEventListener('message', onMsg);
      try { window.parent.postMessage({ type: '__edit_mode_available' }, '*'); } catch (e) {}
      return () => window.removeEventListener('message', onMsg);
    }, []);

    const closePrefs = () => { setPrefsOpen(false); try { window.parent.postMessage({ type: '__edit_mode_dismissed' }, '*'); } catch (e) {} };

    const go = (v) => { setView(v); setDrawer(false); window.scrollTo({ top: 0, behavior: 'auto' }); };
    const switchRole = (r) => {
      if (session && !session.user?.isDevelopmentBypass && window.JS_SESSION.sessionRole(session) !== r) return;
      setRole(r); setView('home'); window.scrollTo(0, 0);
    };
    const logout = () => { window.JS_SESSION.logout(); setSession(null); setAuthed(false); setView('home'); };
    const onAuth = async (nextSession) => {
      setSession(nextSession);
      setRole(window.JS_SESSION.sessionRole(nextSession));
      setAuthed(true);
      try {
        await window.JS_LIVE_DATA.hydrate();
        setAssets([...D.ASSETS]);
        setHasRun(Boolean(D.AUDIT?.id));
        setPackGenerated(Boolean(D.ASSETS && D.ASSETS.length));
      } catch (e) {}
      setView('home');
    };
    const onGenerate = async () => {
      if (D.AUDIT?.id && window.JS_LIVE_DATA) {
        try {
          const generated = await window.JS_LIVE_DATA.generateAssets(D.AUDIT.id);
          setAssets([...generated]);
        } catch (e) {
          alert('Asset generation failed: ' + e.message);
          return;
        }
      }
      setPackGenerated(true);
      go('recommendations');
    };
    const onAuditComplete = async (input) => {
      if (input && window.JS_LIVE_DATA) {
        try {
          await window.JS_LIVE_DATA.createAudit({
            websiteUrl: input.url,
            clientName: input.client,
            industry: input.industry === 'Auto-detect' ? 'auto' : input.industry,
            primaryLocation: input.location,
            maxPages: input.maxPages
          });
          setAssets([...D.ASSETS]);
        } catch (e) {
          alert('Audit failed: ' + e.message);
          return;
        }
      }
      setHasRun(true);
      setPackGenerated(false);
      go('results');
    };
    const startTour = null;

    // auto-launch the guided tour on a first-ever authed visit
    useEffect(() => {}, [authed]);

    // guard: if current view not in this role's nav (and not a shared view), reset
    useEffect(() => {
      const shared = ['results', 'run-audit', 'settings', 'integrations', 'monitoring', 'answer'];
      const ids = (window.NAV[role] || []).flatMap(g => g.items.map(i => i[0]));
      if (!ids.includes(view) && !shared.includes(view)) setView('home');
    }, [role]);

    // Preferences is rendered in ONE stable tree position (always the second
    // child of this fragment) so it is never torn down / remounted when
    // `authed` flips. A remount is what froze the slide-in transition on the
    // first open; keeping the node mounted from initial load lets its closed
    // transform paint early so the first open animates cleanly.
    const viewProps = { view, role, go, audit: D.AUDIT, hasRun, packGenerated, assets, setAssets, onGenerate, onComplete: onAuditComplete, onShip, prefs, openScore, openSteal, startTour };

    return (
      <>
        {!authed ? (
          <Login onAuth={onAuth} />
        ) : (
          <div className={'js-shell' + (collapsed ? ' is-collapsed' : '') + (drawer ? ' is-drawer-open' : '')}>
            <Sidebar role={role} view={view} go={go} collapsed={collapsed} />
            <div className="js-backdrop" onClick={() => setDrawer(false)} />
            <div className="js-main">
              <Topbar role={role} view={view} setRole={switchRole} collapsed={collapsed}
                toggleCollapse={() => setCollapsed(c => !c)} openDrawer={() => setDrawer(true)} openTweaks={() => setPrefsOpen(true)}
                mode={prefs.mode} toggleMode={() => setPref('mode', prefs.mode === 'dark' ? 'light' : 'dark')} session={session} onLogout={logout} />
              <View {...viewProps} />
              <MobileNav role={role} view={view} go={go} />
            </div>
          </div>
        )}
        <Preferences open={prefsOpen} onClose={closePrefs} prefs={prefs} setPref={setPref} onReset={resetPrefs} />
        <ScoreLab focus={scoreFocus} style={prefs.scoreLab} onClose={closeScore} onShip={onShip} />
        <CitationSteal query={stealQuery} onClose={closeSteal} onShip={onShip} />
        {false && <Tour run={tourRun} go={go} onClose={() => setTourRun(false)} />}
      </>
    );
  }

  window.JiffyApp = App;
})();
