// App shell: status spine, left rail, notifications slide-over, login.
const { Pill, Card } = window.UI;

const NAV = [
  { id: 'strategy101', label: 'Bot 101',     icon: Icon.Hash,      hint: '1' },
  { id: 'dashboard',   label: 'Dashboard',   icon: Icon.Dashboard, hint: 'D' },
  { id: 'pairs',       label: 'Pairs',       icon: Icon.Grid,      hint: 'P' },
  { id: 'trades',      label: 'Trades',      icon: Icon.Trades,    hint: 'T' },
  { id: 'config',      label: 'Config',      icon: Icon.Config,    hint: 'C' },
  { id: 'control',     label: 'Control',     icon: Icon.Control,   hint: 'O' },
  { id: 'logs',        label: 'Logs',        icon: Icon.Logs,      hint: 'L' },
  { id: 'replay',      label: 'Replay',      icon: Icon.Replay,    hint: 'R' },
  { id: 'onboarding',  label: 'How it Works', icon: Icon.Info,     hint: 'H' },
];

// Left rail — pin/unpin toggle persists to localStorage.
//
// Default behaviour: pinned (rail stays open at w-52). Click the pin
// icon to collapse to icon-only (w-12) with hover-to-expand. The
// preference survives reloads. Operator-friendly: not everyone wants
// the rail eating 200px of dashboard real estate.
function Sidebar({ route, setRoute, app }) {
  const [pinned, setPinned] = React.useState(() => {
    const v = localStorage.getItem('pb-sidebar-pinned');
    return v === null ? true : v === '1';
  });
  const [hover, setHover] = React.useState(false);
  const open = pinned || hover;
  const togglePin = () => {
    setPinned(p => {
      const next = !p;
      localStorage.setItem('pb-sidebar-pinned', next ? '1' : '0');
      return next;
    });
  };
  return (
    <aside
      onMouseEnter={() => setHover(true)}
      onMouseLeave={() => setHover(false)}
      className={`shrink-0 bg-bg-1 border-r border-line-0 flex flex-col transition-[width] duration-150 ease-out ${open ? 'w-52' : 'w-12'}`}
    >
      {/* brand + pin toggle */}
      <div className="h-12 border-b border-line-0 flex items-center px-3 gap-2 overflow-hidden">
        <div className="w-6 h-6 shrink-0 relative">
          <div className="absolute inset-0 border border-pos-0/70"></div>
          <div className="absolute inset-1 bg-pos-0/30"></div>
          <div className="absolute inset-[3px] bg-pos-0"></div>
        </div>
        <div className={`whitespace-nowrap transition-opacity ${open ? 'opacity-100' : 'opacity-0'} flex-1`}>
          <div className="text-[12px] font-semibold tracking-tight">project-bad</div>
          <div className="text-[9.5px] uppercase tracking-[0.18em] text-ink-3 mono -mt-0.5">v1 · operator</div>
        </div>
        <button
          onClick={togglePin}
          className={`shrink-0 w-6 h-6 grid place-items-center text-ink-3 hover:text-ink-0 hover:bg-bg-2 transition-opacity ${open ? 'opacity-100' : 'opacity-0 pointer-events-none'}`}
          title={pinned ? 'Unpin (auto-collapse)' : 'Pin (always open)'}
        >
          {pinned ? <Icon.Dot size={14} /> : <Icon.ChevronDown size={14} className="rotate-90" />}
        </button>
      </div>

      <nav className="flex-1 py-2">
        {NAV.map(n => {
          const I = n.icon;
          const active = route === n.id;
          return (
            <button
              key={n.id}
              onClick={() => setRoute(n.id)}
              className={`relative w-full flex items-center gap-3 h-9 px-3 text-[12.5px] transition-colors ${active ? 'text-ink-0 bg-bg-2' : 'text-ink-2 hover:text-ink-0 hover:bg-bg-2/60'}`}
            >
              {active && <div className="absolute left-0 top-0 bottom-0 w-[2px] bg-pos-0"></div>}
              <I size={16} className="shrink-0" />
              <span className={`whitespace-nowrap transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`}>{n.label}</span>
              <span className={`ml-auto mono text-[10px] text-ink-4 transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`}>{n.hint}</span>
            </button>
          );
        })}
      </nav>

      {/* footer: version, commit, account */}
      <div className="border-t border-line-0 p-2 mono text-[10px] text-ink-3 space-y-1 overflow-hidden">
        <div className={`flex items-center justify-between transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`}>
          <span>build</span><span className="text-ink-2">v0.5</span>
        </div>
        <div className={`flex items-center justify-between transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`}>
          <span>acct</span><span className="text-ink-2 truncate max-w-[80px]">{app.account}</span>
        </div>
        <div className={`flex justify-center transition-opacity ${open ? 'opacity-0' : 'opacity-100'} -mt-3`}>
          <span>v0.5</span>
        </div>
      </div>
    </aside>
  );
}

// The "status spine" — narrow strip across the very top.
// Mode color owns a 2px tint band that runs the full width.
function StatusSpine({ app, setApp, openNotif, onLogout }) {
  const accounts = app.accounts || [];
  const acct = accounts.find(a => a.id === app.account) || accounts[0] || null;
  const mode = app.modeOverride || (acct && acct.mode) || '';
  const modeBand = { live: 'bg-neg-1', paper: 'bg-amb-1', testnet: 'bg-inf-1' }[mode] || 'bg-line-0';
  const [acctOpen, setAcctOpen] = React.useState(false);
  const acctRef = React.useRef();
  React.useEffect(() => {
    const h = (e) => { if (acctRef.current && !acctRef.current.contains(e.target)) setAcctOpen(false); };
    document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h);
  }, []);

  const stale = app.tickAge > 5;
  const halted = app.halted;
  const unread = app.unreadAlerts;

  return (
    <div className="shrink-0">
      {/* mode tint band */}
      <div className={`h-[2px] ${modeBand}`} />
      {/* main strip */}
      <div className="h-10 bg-bg-1 border-b border-line-0 flex items-center pl-3 pr-2 gap-3">
        {/* account selector */}
        <div className="relative" ref={acctRef}>
          <button onClick={() => setAcctOpen(o => !o)} className="flex items-center gap-2 hover:bg-bg-2 px-2 h-7 border border-line-0 mono text-[11.5px]"
                  disabled={!acct}>
            <span className="text-ink-3">acct</span>
            <span className="text-ink-0">{acct ? acct.id : '—'}</span>
            <span className="text-ink-3">/{acct ? acct.bridge : '—'}</span>
            <Icon.ChevronDown size={11} className="text-ink-3" />
          </button>
          {acctOpen && (
            <div className="absolute top-8 left-0 z-30 w-72 bg-bg-2 border border-line-1 shadow-2xl">
              <div className="px-3 py-2 border-b border-line-0 text-[10px] uppercase tracking-[0.14em] text-ink-3">accounts</div>
              {accounts.length === 0 && (
                <div className="px-3 py-3 mono text-[11px] text-ink-3">no accounts configured</div>
              )}
              {accounts.map(a => (
                <button key={a.id}
                  onClick={() => { setApp(s => ({ ...s, account: a.id, modeOverride: null })); setAcctOpen(false); }}
                  className={`w-full text-left px-3 py-2 hover:bg-bg-3 flex items-center gap-3 ${app.account === a.id ? 'bg-bg-3/60' : ''}`}>
                  <div className={`w-1 h-8 ${ {live:'bg-neg-1', paper:'bg-amb-1', testnet:'bg-inf-1'}[a.mode] || 'bg-line-0' }`}></div>
                  <div className="flex-1">
                    <div className="mono text-[12px] text-ink-0">{a.id}</div>
                    <div className="mono text-[10.5px] text-ink-3">{a.exchange} · bridge {a.bridge}{a.current_coin ? ` · holds ${a.current_coin}` : ''}</div>
                  </div>
                  <Pill tone={a.mode}>{a.mode}</Pill>
                </button>
              ))}
            </div>
          )}
        </div>

        {mode && <Pill tone={mode} dot>{mode}</Pill>}

        {/* kill-switch — clickable when halted so the operator lands on the
            Control page (where Resume lives) with one tap. */}
        {halted ? (
          <button
            onClick={() => setApp(s => ({ ...s, _navTo: 'control', _navAt: Date.now() }))}
            className="flex items-center gap-1.5 mono text-[11px] px-2 h-6 border border-neg-1/70 bg-neg-2/40 text-neg-0 hover:bg-neg-2/70 cursor-pointer animate-pulse-slow"
            title={`halted: ${halted} · click to open Control page`}
          >
            <span className="w-1.5 h-1.5 bg-neg-0"></span>
            <span className="uppercase tracking-[0.1em] text-[10px] font-semibold">halted</span>
            <span className="text-ink-2 truncate max-w-[260px]">· {halted}</span>
            <Icon.ArrowRight size={11} className="text-neg-0" />
          </button>
        ) : (
          <div
            className="flex items-center gap-1.5 mono text-[11px] px-2 h-6 border border-pos-1/40 bg-pos-2/15 text-pos-0"
            title="running"
          >
            <span className="w-1.5 h-1.5 bg-pos-0"></span>
            <span className="uppercase tracking-[0.1em] text-[10px]">running</span>
          </div>
        )}

        {/* last tick — seconds since the most recent BBO frame. null
            means we've never seen one (badge hidden); 0 means
            "<1s ago" which is actually the most reassuring state. */}
        {app.tickAge != null && (
          <div className={`flex items-center gap-1.5 mono text-[11px] ${stale ? 'text-amb-0' : 'text-ink-2'}`} title="seconds since the last BBO frame">
            <span className="text-ink-3 text-[10px] uppercase tracking-[0.1em]">tick</span>
            <span>{app.tickAge < 1 ? '<1s' : `${app.tickAge}s`}</span>
          </div>
        )}

        {/* scout iter rate — backend publishes 1 SSE 'scout' frame per
            second carrying iter_per_sec. We hide the badge entirely
            until the bot has had a chance to scout (post-boot). */}
        {app.scoutRate > 0 && (
          <div className="flex items-center gap-1.5 mono text-[11px] text-ink-2">
            <span className="text-ink-3 text-[10px] uppercase tracking-[0.1em]">scout</span>
            <span>{app.scoutRate}/s</span>
          </div>
        )}

        <div className="flex-1"></div>

        {/* clock */}
        <div className="mono text-[11px] text-ink-2">{window.UI.fmtTs(app.now)}</div>

        {/* notifications bell */}
        <button onClick={openNotif} className="relative h-7 w-7 grid place-items-center text-ink-2 hover:text-ink-0 hover:bg-bg-2">
          <Icon.Bell size={15} />
          {unread > 0 && (
            <span className="absolute top-0.5 right-0.5 min-w-[14px] h-[14px] px-1 bg-amb-0 text-bg-0 text-[9px] mono font-semibold grid place-items-center">{unread}</span>
          )}
        </button>

        {/* user menu */}
        <UserMenu app={app} onLogout={onLogout} />
      </div>
    </div>
  );
}

function UserMenu({ app, onLogout }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef();
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h); return () => document.removeEventListener('mousedown', h);
  }, []);
  return (
    <div className="relative" ref={ref}>
      <button onClick={() => setOpen(o => !o)} className="h-7 px-2 flex items-center gap-2 hover:bg-bg-2 border border-line-0">
        <div className="w-4 h-4 grid place-items-center bg-bg-3 mono text-[9px] text-ink-1 font-semibold">OP</div>
        <span className="mono text-[11px] text-ink-1">{app.user}</span>
        <Icon.ChevronDown size={11} className="text-ink-3" />
      </button>
      {open && (
        <div className="absolute top-8 right-0 z-30 w-48 bg-bg-2 border border-line-1 py-1">
          <div className="px-3 py-2 border-b border-line-0">
            <div className="mono text-[12px] text-ink-0">{app.user}</div>
            <div className="mono text-[10.5px] text-ink-3">single-user · local</div>
          </div>
          <button className="w-full text-left px-3 py-1.5 hover:bg-bg-3 flex items-center gap-2 text-[12px]">
            <Icon.User size={13} className="text-ink-3" /> account
          </button>
          <button
            onClick={async () => {
              if (typeof onLogout === 'function') {
                try { await onLogout(); } catch (_) { /* fall through to reload */ }
              }
              location.reload();
            }}
            className="w-full text-left px-3 py-1.5 hover:bg-bg-3 flex items-center gap-2 text-[12px] text-ink-1">
            <Icon.Logout size={13} className="text-ink-3" /> log out
          </button>
        </div>
      )}
    </div>
  );
}

// Right-side slide-over for unread events / dead-letter / reconcile diffs.
function NotificationsPanel({ open, onClose, alerts }) {
  return (
    <>
      <div className={`fixed inset-0 z-40 transition-opacity ${open ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}
           style={{ background: 'rgba(0,0,0,0.4)' }} onClick={onClose} />
      <aside className={`fixed top-0 right-0 bottom-0 w-[420px] bg-bg-1 border-l border-line-1 z-50 transition-transform duration-200 ease-out ${open ? 'translate-x-0' : 'translate-x-full'}`}>
        <header className="h-12 flex items-center justify-between px-4 border-b border-line-0">
          <div className="flex items-center gap-2">
            <Icon.Bell size={14} className="text-ink-2"/>
            <h3 className="text-[12px] uppercase tracking-[0.16em] text-ink-1 font-medium">notifications</h3>
            <span className="mono text-[11px] text-ink-3">{alerts.length} open</span>
          </div>
          <button onClick={onClose} className="p-1 text-ink-2 hover:text-ink-0"><Icon.X size={14}/></button>
        </header>
        <div className="overflow-auto scrollbar h-[calc(100%-3rem)]">
          {alerts.map((a, i) => {
            const tone = a.severity === 'error' ? 'neg' : a.severity === 'warn' ? 'amb' : 'inf';
            return (
              <div key={i} className="p-4 border-b border-line-0 hover:bg-bg-2/50">
                <div className="flex items-start gap-3">
                  <div className={`mt-0.5 ${tone === 'neg' ? 'text-neg-0' : tone === 'amb' ? 'text-amb-0' : 'text-inf-0'}`}>
                    {tone === 'inf' ? <Icon.Info size={14}/> : <Icon.AlertTri size={14}/>}
                  </div>
                  <div className="flex-1">
                    <div className="flex items-center gap-2">
                      <Pill tone={tone}>{a.kind.replace('_',' ')}</Pill>
                      <span className="mono text-[10.5px] text-ink-3">{window.UI.since(a.at)}</span>
                    </div>
                    <div className="mt-2 mono text-[12px] text-ink-1 leading-relaxed">{a.reason}</div>
                    <div className="mt-1.5 flex items-center gap-3 mono text-[10.5px] text-ink-3">
                      <span>{a.account}</span>
                      <span>{window.UI.fmtTs(a.at)}</span>
                      {a.cid && <window.UI.Copy text={a.cid} display={a.cid.slice(0,16)+'…'} />}
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
          {!alerts.length && (
            <div className="p-8 text-center mono text-[11px] text-ink-3">no open notifications</div>
          )}
        </div>
      </aside>
    </>
  );
}

function LoginScreen({ onLogin, authEnabled }) {
  const [u, setU] = React.useState('operator');
  const [p, setP] = React.useState('');
  const [remember, setRemember] = React.useState(true);
  const [err, setErr] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const submit = async (e) => {
    e.preventDefault();
    if (!p.length && authEnabled !== false) {
      setErr('password is required');
      return;
    }
    setBusy(true); setErr('');
    try {
      await onLogin(u, p);
    } catch (e) {
      setErr(e.message || 'authentication failed');
      setBusy(false);
    }
  };
  return (
    <div className="min-h-screen grid place-items-center grid-bg">
      <div className="w-[380px]">
        <div className="flex items-center gap-3 mb-8">
          <div className="w-7 h-7 relative">
            <div className="absolute inset-0 border border-pos-0/70"></div>
            <div className="absolute inset-1 bg-pos-0/30"></div>
            <div className="absolute inset-[3px] bg-pos-0"></div>
          </div>
          <div>
            <div className="text-[15px] font-semibold">project-bad</div>
            <div className="text-[10px] uppercase tracking-[0.2em] text-ink-3 mono -mt-0.5">v1 · operator</div>
          </div>
        </div>
        <div className="bg-bg-1 border border-line-0">
          <div className="px-5 pt-5 pb-3 border-b border-line-0">
            <h1 className="text-[14px] text-ink-0 font-medium">sign in</h1>
            <p className="text-[11.5px] text-ink-3 mono mt-1">
              {authEnabled === false
                ? 'auth disabled · dev mode · any password works'
                : 'single-user · local · session cookie'}
            </p>
          </div>
          <form onSubmit={submit} className="p-5 space-y-4">
            <label className="block">
              <span className="text-[10.5px] uppercase tracking-[0.14em] text-ink-3 mono">username</span>
              <input value={u} onChange={e => setU(e.target.value)} autoFocus disabled={busy}
                className="mt-1 w-full bg-bg-2 border border-line-1 mono text-[13px] px-3 h-9 text-ink-0 focus:border-pos-1 outline-none disabled:opacity-60" />
            </label>
            <label className="block">
              <span className="text-[10.5px] uppercase tracking-[0.14em] text-ink-3 mono">password</span>
              <input type="password" value={p} onChange={e => setP(e.target.value)} placeholder="••••••••" disabled={busy}
                className="mt-1 w-full bg-bg-2 border border-line-1 mono text-[13px] px-3 h-9 text-ink-0 focus:border-pos-1 outline-none disabled:opacity-60" />
            </label>
            <label className="flex items-center gap-2 mono text-[11.5px] text-ink-1 cursor-pointer">
              <input type="checkbox" checked={remember} onChange={e => setRemember(e.target.checked)}
                className="w-3.5 h-3.5 accent-pos-0 bg-bg-2" />
              remember me on this machine
            </label>
            {err && (
              <div className="mono text-[11px] text-neg-0 border border-neg-1/40 bg-neg-2/15 px-2 py-1.5">
                {err}
              </div>
            )}
            <button type="submit" disabled={busy}
              className="w-full h-9 bg-pos-2 border border-pos-1 hover:bg-pos-1 text-ink-0 mono text-[12px] uppercase tracking-[0.14em] transition-colors disabled:opacity-60 disabled:cursor-not-allowed">
              {busy ? 'authenticating…' : 'authenticate'}
            </button>
          </form>
        </div>
        <div className="mt-3 flex justify-between mono text-[10.5px] text-ink-3">
          <span>no signup · no password reset</span>
          <span>v0.4.1 · 9a17c4e</span>
        </div>
      </div>
    </div>
  );
}

window.Shell = { Sidebar, StatusSpine, NotificationsPanel, LoginScreen, NAV };
