/* ===========================================================
   App shell: top bar, sidebar, hash-router, theme, ⌘K
   =========================================================== */

const { useState: useStateA, useEffect: useEffectA, useMemo: useMemoA, useRef: useRefA, useCallback: useCallbackA } = React;

/* --------- Theme (persisted) --------- */
function useTheme() {
  const [theme, setTheme] = useStateA(() => {
    try {
      return localStorage.getItem('newsroom.theme') || 'dark';
    } catch { return 'dark'; }
  });
  useEffectA(() => {
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem('newsroom.theme', theme); } catch {}
  }, [theme]);
  return [theme, setTheme];
}

/* --------- Hash router --------- */
function useHashRoute() {
  const [path, setPath] = useStateA(() => window.location.hash.slice(1) || '/');
  useEffectA(() => {
    const handler = () => setPath(window.location.hash.slice(1) || '/');
    window.addEventListener('hashchange', handler);
    return () => window.removeEventListener('hashchange', handler);
  }, []);
  const nav = useCallbackA((to) => {
    window.location.hash = to;
  }, []);
  return [path, nav];
}

/* --------- Sidebar (persisted) --------- */
function useSidebar() {
  const [collapsed, setCollapsed] = useStateA(() => {
    try { return localStorage.getItem('newsroom.sidebar') === 'collapsed'; } catch { return false; }
  });
  useEffectA(() => {
    try { localStorage.setItem('newsroom.sidebar', collapsed ? 'collapsed' : 'expanded'); } catch {}
  }, [collapsed]);
  return [collapsed, setCollapsed];
}

/* --------- Sidebar nav config --------- */
const NAV = [
  { to: '/',         label: 'Overview',  icon: 'layout',   match: p => p === '/' },
  { to: '/pipeline', label: 'Pipeline',  icon: 'funnel',   match: p => p === '/pipeline' },
  { to: '/runs',     label: 'Runs',      icon: 'list',     match: p => p === '/runs' || /^\/runs\/[^/]+$/.test(p) },
  { to: '/terminal', label: 'Terminal',  icon: 'terminal', match: p => p === '/terminal' },
  { to: '/ranking',  label: 'Ranking',   icon: 'trophy',   match: p => p === '/ranking' },
  { to: '/arena',    label: 'Arena',     icon: 'swords',   match: p => p === '/arena' },
  { to: '/visor',    label: 'Visor',     icon: 'retry',    match: p => /\/visor$/.test(p) },
  { to: '/sources',  label: 'Sources',   icon: 'newspaper',match: p => p === '/sources' },
  { to: '/settings', label: 'Settings',  icon: 'settings', match: p => p === '/settings' },
];

/* --------- Command palette --------- */
function CommandPalette({ open, onClose, runs, nav, toggleTheme, toggleSidebar }) {
  const [q, setQ] = useStateA('');
  const [sel, setSel] = useStateA(0);
  const inputRef = useRefA(null);

  useEffectA(() => {
    if (open) {
      setQ(''); setSel(0);
      setTimeout(() => inputRef.current?.focus(), 50);
    }
  }, [open]);

  const items = useMemoA(() => {
    const navItems = [
      { kind: 'nav', label: 'Overview',        sub: 'g o',  to: '/' },
      { kind: 'nav', label: 'Pipeline',        sub: 'g p',  to: '/pipeline' },
      { kind: 'nav', label: 'Runs',            sub: 'g r',  to: '/runs' },
      { kind: 'nav', label: 'Terminal',        sub: 'g t',  to: '/terminal' },
      { kind: 'nav', label: 'Ranking',         sub: 'g k',  to: '/ranking' },
      { kind: 'nav', label: 'Arena',           sub: 'g a',  to: '/arena' },
      { kind: 'nav', label: 'Sources',         sub: 'g s',  to: '/sources' },
      { kind: 'nav', label: 'Settings',        sub: '',     to: '/settings' },
    ];
    const actions = [
      { kind: 'action', label: 'Abrir último run',    sub: 'enter', do: () => nav(`/runs/${runs[0].id}`) },
      { kind: 'action', label: 'Abrir visor del último', sub: '',  do: () => nav(`/runs/${runs[0].id}/visor`) },
      { kind: 'action', label: 'Cambiar tema (claro/oscuro)', sub: 't', do: () => toggleTheme() },
      { kind: 'action', label: 'Plegar / desplegar sidebar', sub: '[',  do: () => toggleSidebar() },
      { kind: 'action', label: 'Copiar JSON del último run', sub: '', do: () => {
          navigator.clipboard?.writeText(JSON.stringify(runs[0], null, 2));
          window.__toast?.('JSON copiado', 'ok');
        } },
    ];
    const runItems = runs.map(r => ({
      kind: 'run',
      label: r.editor.selected_titulo,
      sub: `${r.id} · ${fmtClock(r.timestamp)}`,
      to: `/runs/${r.id}`,
    }));
    const all = [...navItems, ...actions, ...runItems];
    if (!q) return all;
    const lower = q.toLowerCase();
    return all.filter(i => i.label.toLowerCase().includes(lower) || (i.sub||'').toLowerCase().includes(lower));
  }, [q, runs, nav, toggleTheme, toggleSidebar]);

  const onKey = (e) => {
    if (e.key === 'ArrowDown') { e.preventDefault(); setSel(s => Math.min(s+1, items.length-1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setSel(s => Math.max(s-1, 0)); }
    else if (e.key === 'Enter') {
      e.preventDefault();
      const it = items[sel];
      if (!it) return;
      if (it.to) nav(it.to);
      else if (it.do) it.do();
      onClose();
    } else if (e.key === 'Escape') {
      onClose();
    }
  };

  if (!open) return null;
  return (
    <div className="cmd-backdrop" onClick={onClose}>
      <div className="cmd-modal" onClick={e => e.stopPropagation()}>
        <div className="cmd-input-wrap">
          <I.search style={{color: 'var(--text3)'}} />
          <input
            ref={inputRef}
            value={q}
            onChange={e => { setQ(e.target.value); setSel(0); }}
            onKeyDown={onKey}
            placeholder="Buscar acciones, runs, vistas…"
          />
          <kbd className="mono" style={{fontSize: 10, padding:'2px 6px', border:'1px solid var(--border)', borderRadius:3, color:'var(--text3)'}}>esc</kbd>
        </div>
        <div className="cmd-list">
          {items.length === 0 && <div className="cmd-item">Sin resultados</div>}
          {items.map((it, i) => {
            const groupLabel = i === 0 ? it.kind :
              (items[i-1].kind !== it.kind ? it.kind : null);
            return (
              <React.Fragment key={i}>
                {groupLabel && <div className="cmd-group-label">
                  {groupLabel === 'nav' ? 'Navegación' : groupLabel === 'action' ? 'Acciones' : 'Runs'}
                </div>}
                <div
                  className={`cmd-item ${i === sel ? 'cmd-sel' : ''}`}
                  onMouseEnter={() => setSel(i)}
                  onClick={() => { if (it.to) nav(it.to); else if (it.do) it.do(); onClose(); }}
                >
                  {it.kind === 'nav' && <I.chevron className="icon" />}
                  {it.kind === 'action' && <I.spark className="icon" />}
                  {it.kind === 'run' && <I.newspaper className="icon" />}
                  <span style={{flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}>{it.label}</span>
                  {it.sub && <span className="sub">{it.sub}</span>}
                </div>
              </React.Fragment>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* --------- Toasts --------- */
function ToastHost() {
  const [toasts, setToasts] = useStateA([]);
  useEffectA(() => {
    window.__toast = (msg, kind='info') => {
      const id = Date.now() + Math.random();
      setToasts(t => [...t, { id, msg, kind }]);
      setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3200);
    };
  }, []);
  return (
    <div className="toasts">
      {toasts.map(t => (
        <div key={t.id} className={`toast ${t.kind}`}>
          <span className="icon">
            {t.kind === 'ok' ? <I.check /> : t.kind === 'warn' ? <I.alert /> : t.kind === 'err' ? <I.x /> : <I.spark />}
          </span>
          {t.msg}
        </div>
      ))}
    </div>
  );
}

/* --------- App --------- */
function App() {
  const [theme, setTheme] = useTheme();
  const [collapsed, setCollapsed] = useSidebar();
  const [path, nav] = useHashRoute();
  const [cmdOpen, setCmdOpen] = useStateA(false);

  const [runs, setRuns] = useStateA(() => getMockExecutionHistory());
  const [logs, setLogs] = useStateA(() => getMockLogs());

  // Poll bridge for real runs every 4s, fallback to mock when offline
  useEffectA(() => {
    if (!window.bridgeFetchRuns) return;
    let cancelled = false;
    async function poll() {
      const bridgeRuns = await window.bridgeFetchRuns();
      if (!cancelled && bridgeRuns && bridgeRuns.length > 0) {
        setRuns(bridgeRuns);
      }
    }
    poll();
    const id = setInterval(poll, 4000);
    return () => { cancelled = true; clearInterval(id); };
  }, []);

  // Connect WebSocket for live terminal logs
  useEffectA(() => {
    if (!window.connectBridgeLogs) return;
    const disconnect = window.connectBridgeLogs(
      (line) => setLogs(prev => [line, ...prev.slice(0, 999)]),
      (status) => {
        if (status === 'connected')     window.__toast?.('Bridge conectado', 'ok');
        if (status === 'disconnected')  window.__toast?.('Bridge desconectado — usando datos locales', 'warn');
        if (status.startsWith('rotated:')) window.__toast?.('Nuevo log: ' + status.split(':')[1], 'info');
      }
    );
    return disconnect;
  }, []);

  // periodic toasts so the dashboard feels alive
  useEffectA(() => {
    const id1 = setTimeout(() => window.__toast?.('Run completado · publicado #24812', 'ok'), 4000);
    const id2 = setTimeout(() => window.__toast?.('Anomalía detectada en run-002', 'warn'), 11000);
    return () => { clearTimeout(id1); clearTimeout(id2); };
  }, []);

  // keyboard
  useEffectA(() => {
    let gMode = false;
    const onKey = (e) => {
      // ⌘K / ctrl K
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        setCmdOpen(o => !o);
        return;
      }
      if (e.key === 'Escape') {
        setCmdOpen(false);
        return;
      }
      if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
      if (e.key.toLowerCase() === 'g') { gMode = true; setTimeout(() => gMode = false, 800); return; }
      if (gMode) {
        const map = { o: '/', p: '/pipeline', r: '/runs', t: '/terminal', k: '/ranking', a: '/arena', s: '/sources' };
        const to = map[e.key.toLowerCase()];
        if (to) { nav(to); gMode = false; }
        return;
      }
      if (e.key === 't') {
        setTheme(t => t === 'dark' ? 'light' : 'dark');
      }
      if (e.key === '[') {
        setCollapsed(c => !c);
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [nav]);

  // route resolution
  const view = useMemoA(() => {
    if (path === '/' || path === '') return <OverviewView runs={runs} nav={nav} />;
    if (path === '/pipeline')        return <PipelineView runs={runs} nav={nav} />;
    if (path === '/runs')            return <RunsView runs={runs} nav={nav} />;
    if (path === '/terminal')        return <TerminalView logs={logs} />;
    if (path === '/ranking')         return <RankingView runs={runs} />;
    if (path === '/arena')           return <ArenaView runs={runs} />;
    if (path === '/sources')         return <SourcesView runs={runs} />;
    if (path === '/settings')        return <SettingsView />;
    if (path === '/visor') { nav(`/runs/${runs[0].id}/visor`); return null; }
    const m1 = path.match(/^\/runs\/([^/]+)\/visor$/);
    if (m1) {
      const r = runs.find(r => r.id === m1[1]);
      if (r) return <VisorView run={r} nav={nav} />;
    }
    const m2 = path.match(/^\/runs\/([^/]+)$/);
    if (m2) {
      const r = runs.find(r => r.id === m2[1]);
      if (r) return <RunDetailView run={r} nav={nav} />;
    }
    return <div className="page"><div className="empty"><div className="ico">404</div><div>Ruta no encontrada: <code>{path}</code></div></div></div>;
  }, [path, runs, logs, nav]);

  const latest = runs[0];
  const sidebarLabel = (icon) => {
    const map = {
      layout: <I.layout />, funnel: <I.funnel />, list: <I.list />,
      terminal: <I.terminal />, trophy: <I.trophy />, swords: <I.swords />,
      retry: <I.retry />, newspaper: <I.newspaper />, settings: <I.settings />,
    };
    return map[icon];
  };

  return (
    <div className="app" data-sidebar={collapsed ? 'collapsed' : 'expanded'}>
      {/* TOP BAR */}
      <div className="topbar">
        <div className="brand" onClick={() => setCollapsed(c => !c)} style={{cursor: 'pointer'}} title="Pulsa para plegar/desplegar la barra lateral">
          <div className="full">
            <div style={{fontFamily:'var(--mono)', fontSize: 8, color: 'var(--text4)', letterSpacing: '0.22em'}}>SALA DE REDACCIÓN</div>
            <div>NEWSROOM <span className="slash">//</span> MONITOR</div>
          </div>
          <div style={{display: collapsed ? 'block' : 'none', color: 'var(--masthead)', fontSize: 18, fontWeight: 900}}>N</div>
        </div>

        <div className="health">
          <div className="item" data-tip="Ollama bridge en localhost:11434">
            <span className="statusdot ok" />
            <span className="lbl">Ollama</span>
            <b>ok</b>
          </div>
          <div className="item" data-tip="vRAM consumida por modelos cargados">
            <span className="lbl">GPU</span>
            <span className="gauge"><i style={{width: '71%'}} /></span>
            <b>71%</b>
          </div>
          <div className="item" data-tip="qwen2.5:14b · mistral-nemo:12b · gemma4:e4b">
            <span className="lbl">Modelos</span>
            <b>3</b>
          </div>
          <div className="item" data-tip="Latencia media por llamada (P50)">
            <span className="lbl">LLM lat</span>
            <b>2.1s</b>
          </div>
          <div className="item" data-tip="Throughput de pipeline">
            <span className="lbl">Runs/h</span>
            <b>3.4</b>
          </div>
          <div style={{flex: 1}} />
          <div className="item" style={{color: 'var(--text3)'}}>
            <span className="statusdot ok" /> bridge ws · publica cada 4s
          </div>
        </div>

        <div className="actions">
          <button className="cmdk" onClick={() => setCmdOpen(true)}>
            <I.search /> Buscar… <kbd>⌘K</kbd>
          </button>
          <button className="iconbtn" data-tip="3 notificaciones">
            <I.bell />
            <span className="notif-dot">3</span>
          </button>
          <button
            className="iconbtn"
            data-tip={theme === 'dark' ? 'Cambiar a tema claro (t)' : 'Cambiar a tema oscuro (t)'}
            onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
          >
            {theme === 'dark' ? <I.sun /> : <I.moon />}
          </button>
        </div>
      </div>

      {/* SIDEBAR */}
      <div className="sidebar">
        <div className="navlist">
          {!collapsed && <div className="section-label">Vistas</div>}
          {NAV.map(n => (
            <div
              key={n.to}
              className={`navitem ${n.match(path) ? 'active' : ''}`}
              onClick={() => {
                if (n.to === '/visor') nav(`/runs/${latest.id}/visor`);
                else nav(n.to);
              }}
              title={collapsed ? n.label : ''}
            >
              <span className="icon">{sidebarLabel(n.icon)}</span>
              <span className="lbl">{n.label}</span>
              {n.to === '/runs' && <span className="badge-count">{runs.length}</span>}
              {n.to === '/terminal' && <span className="badge-count" style={{color: 'var(--terminal)'}}>●</span>}
            </div>
          ))}
        </div>
        <div className="last-run">
          <div className="label">Último run</div>
          <div className="row">
            <StatusDot status={latest.status === 'success' ? 'ok' : latest.status === 'partial' ? 'warn' : 'err'} />
            <span>{fmtClock(latest.timestamp)}</span>
            <span style={{marginLeft: 'auto', fontSize: 10, color: 'var(--text3)'}}>{fmtAgo(latest.timestamp)}</span>
          </div>
          <div style={{fontSize: 10.5, color: 'var(--text3)', fontStyle: 'italic', fontFamily: 'var(--serif)', overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>
            {latest.editor.selected_titulo}
          </div>
        </div>
      </div>

      {/* MAIN */}
      <div className="main">
        {view}
      </div>

      <CommandPalette
        open={cmdOpen}
        onClose={() => setCmdOpen(false)}
        runs={runs}
        nav={nav}
        toggleTheme={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
        toggleSidebar={() => setCollapsed(c => !c)}
      />
      <ToastHost />
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
