/* global React, RC, ClipEditor, Hls, DevicesTab, ClassifyBroadcastModal, IncomingBroadcastWatcher */
// Admin — Editorial Race-Control Console.
//
// Layout:
//   - sticky top masthead with stream selector
//   - segmented tab strip
//   - one focused panel at a time, plenty of negative space
//
// Goals: fewer clicks, single point of focus, hairline rules instead of
// boxes-within-boxes. Same Italian-magazine DNA as the public viewer.

const { useState: useSA, useEffect: useEA, useMemo: useMA, useRef: useRA, useCallback: useCA } = React;

// ── Top-level shell ──────────────────────────────────────────────────
function LiveAdmin({ me, onLogout, toast }) {
  const [tab, setTab] = useSA('stream');
  const [streams, setStreams] = useSA([]);
  const [streamId, setStreamId] = useSA(null);
  const [creatingStream, setCreatingStream] = useSA(false);
  const [streamPickerOpen, setStreamPickerOpen] = useSA(false);

  const refreshStreams = useCA(async () => {
    const list = await RC.apiFetch('/api/streams');
    setStreams(list);
    if (!streamId && list.length) setStreamId(list[0].id);
  }, [streamId]);

  useEA(() => { refreshStreams().catch((e) => toast?.(e.message, 'error')); }, []);
  // Light auto-refresh so live state / current broadcast updates without
  // requiring the user to flip tabs.
  useEA(() => {
    const t = setInterval(() => refreshStreams().catch(() => {}), 5000);
    return () => clearInterval(t);
  }, [refreshStreams]);

  const stream = useMA(() => streams.find((s) => s.id === streamId) || null, [streams, streamId]);

  const tabs = [
    { id: 'stream',   num: '01', label: 'Live Stream' },
    { id: 'replays',  num: '02', label: 'Replays' },
    { id: 'clips',    num: '03', label: 'Clips' },
    { id: 'sessions', num: '04', label: 'Sessions' },
    { id: 'overlay',  num: '05', label: 'Overlay' },
    { id: 'devices',  num: '06', label: 'Devices' },
    { id: 'settings', num: '07', label: 'Settings' },
  ];

  return (
    <div className="ed-app ed-root">
      <div className="ed-grain" aria-hidden="true" />

      <header className="ed-app__masthead">
        <div className="ed-app__masthead-row">
          <div>
            <span className="ed-app__logo">RaceCast</span>
            <span className="ed-app__logo-sub">Race Control</span>
          </div>

          <div style={{ display: 'flex', justifyContent: 'center', position: 'relative' }}>
            {stream ? (
              <button className="ed-stream-pill" onClick={() => setStreamPickerOpen(!streamPickerOpen)}>
                <span className="ed-app__logo-sub" style={{ marginLeft: 0, marginRight: 8 }}>Stream</span>
                <span className="ed-stream-pill__name">{stream.name}</span>
                {stream.location && <span className="ed-stream-pill__loc">{stream.location}</span>}
                <span className="ed-stream-pill__caret">{streamPickerOpen ? '▴' : '▾'}</span>
              </button>
            ) : (
              <button className="ed-btn ed-btn--ferrari" onClick={() => setCreatingStream(true)}>
                + new stream
              </button>
            )}
            {streamPickerOpen && (
              <StreamPicker
                streams={streams}
                onPick={(id) => { setStreamId(id); setStreamPickerOpen(false); }}
                onClose={() => setStreamPickerOpen(false)}
                onCreate={() => { setStreamPickerOpen(false); setCreatingStream(true); }}
              />
            )}
          </div>

          <div className="ed-app__user">
            <span style={{ whiteSpace: 'nowrap' }}>{me.team?.name || ''}</span>
            <span style={{ color: 'var(--ed-cream-trace)' }}>·</span>
            <span style={{ whiteSpace: 'nowrap', color: 'var(--ed-cream-faint)' }}>{me.user.email}</span>
            <button className="ed-btn ed-btn--ghost" onClick={onLogout} style={{ marginLeft: 8 }}>logout</button>
          </div>
        </div>

        <nav className="ed-app__tabs">
          {tabs.map((t) => (
            <button key={t.id}
                    className="ed-tab"
                    aria-current={tab === t.id ? 'true' : 'false'}
                    onClick={() => setTab(t.id)}>
              <span className="ed-tab__num">{t.num}</span>
              <span>{t.label}</span>
            </button>
          ))}
        </nav>
      </header>

      <main className="ed-app__main">
        {!stream ? (
          <EmptyState onCreate={() => setCreatingStream(true)} />
        ) : (<>
          {tab === 'stream'   && <StreamTab stream={stream} onChange={refreshStreams} toast={toast} />}
          {tab === 'sessions' && <SessionsTab streams={streams} toast={toast} />}
          {tab === 'replays'  && <ReplaysTab stream={stream} toast={toast} />}
          {tab === 'clips'    && <ClipsTab stream={stream} toast={toast} />}
          {tab === 'overlay'  && <OverlayTab stream={stream} toast={toast} />}
          {tab === 'devices'  && <DevicesTab streams={streams} toast={toast} />}
          {tab === 'settings' && <SettingsTab toast={toast} onTeamUpdated={refreshStreams} />}
        </>)}
      </main>

      {creatingStream && <CreateStreamModal
        onClose={() => setCreatingStream(false)}
        onCreated={async (newStream) => {
          await refreshStreams();
          setStreamId(newStream.id);
          setCreatingStream(false);
        }}
        toast={toast} />}

      <IncomingBroadcastWatcher
        toast={toast}
        onClassified={refreshStreams}
        onRejected={refreshStreams}
      />
    </div>
  );
}

function EmptyState({ onCreate }) {
  return (
    <div style={{ padding: '120px 0', textAlign: 'center' }}>
      <div className="ed-card__lead">No streams yet</div>
      <h1 className="ed-page-head__title" style={{ marginBottom: 18 }}>
        Set up your <em>first</em> channel.
      </h1>
      <p style={{ fontFamily: 'var(--ed-body)', fontSize: 15, color: 'var(--ed-cream-dim)', maxWidth: 460, margin: '0 auto 28px' }}>
        Ein Stream ist die langfristige Identität einer Cam. Der Stream-Key ist
        gleichzeitig das Push-Passwort. Erst wenn ein Push reinkommt entsteht ein
        klassifizierbarer Broadcast.
      </p>
      <button className="ed-btn ed-btn--ferrari" onClick={onCreate}>+ create stream</button>
    </div>
  );
}

function StreamPicker({ streams, onPick, onClose, onCreate }) {
  // close on outside click
  useEA(() => {
    const onDoc = (e) => { if (!e.target.closest('.ed-stream-picker')) onClose(); };
    setTimeout(() => document.addEventListener('click', onDoc), 0);
    return () => document.removeEventListener('click', onDoc);
  }, []);
  return (
    <div className="ed-stream-picker" style={{
      position: 'absolute', top: 'calc(100% + 8px)', left: '50%', transform: 'translateX(-50%)',
      minWidth: 360,
      background: 'var(--ed-paper)',
      border: '1px solid var(--ed-rule-strong)',
      borderTop: '2px solid var(--ed-cream)',
      zIndex: 60,
      animation: 'ed-toast-in 180ms ease',
    }}>
      <div style={{ padding: '14px 18px 8px' }} className="ed-card__lead">Streams</div>
      <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
        {streams.map((s) => (
          <li key={s.id}>
            <button onClick={() => onPick(s.id)} style={{
              width: '100%', textAlign: 'left',
              padding: '12px 18px',
              background: 'transparent',
              border: 0, borderTop: '1px solid var(--ed-rule)',
              cursor: 'pointer',
              display: 'grid', gridTemplateColumns: '1fr auto', gap: 12, alignItems: 'baseline',
              fontFamily: 'var(--ed-body)', color: 'var(--ed-cream)',
            }}>
              <span>
                <span style={{ fontSize: 14 }}>{s.name}</span>
                {s.location && <span style={{ marginLeft: 8, fontStyle: 'italic', color: 'var(--ed-cream-faint)', fontSize: 12 }}>{s.location}</span>}
              </span>
              {s.currentBroadcast?.status === 'live'
                ? <span className="ed-pill ed-pill--live">live</span>
                : <span style={{ fontFamily: 'var(--ed-mono)', fontSize: 10, color: 'var(--ed-cream-faint)' }}>—</span>}
            </button>
          </li>
        ))}
      </ul>
      <button onClick={onCreate} style={{
        width: '100%', padding: '14px 18px',
        background: 'transparent', border: 0,
        borderTop: '1px solid var(--ed-rule-strong)',
        color: 'var(--ed-ferrari)', cursor: 'pointer',
        fontFamily: 'var(--ed-body)', fontSize: 12, letterSpacing: '0.18em',
        textTransform: 'uppercase', textAlign: 'left',
      }}>
        + new stream
      </button>
    </div>
  );
}

// ─── Stream tab ──────────────────────────────────────────────────────
function StreamTab({ stream, onChange, toast }) {
  const [state, setState] = useSA(stream.state);
  const [bitrateKbps, setBitrateKbps] = useSA(null);
  const [readers, setReaders] = useSA(0);
  const [tele, setTele] = useSA({ speedKmh: null });
  const [showEndpoints, setShowEndpoints] = useSA(false);
  const [showSettings, setShowSettings] = useSA(false);
  const [editingBroadcast, setEditingBroadcast] = useSA(false);

  useEA(() => setState(stream.state), [stream.state]);

  useEA(() => {
    const conn = RC.openWs({
      token: RC.getToken(),
      handlers: {
        onOpen: () => conn.subscribe(stream.id),
        onMessage: (type, msg) => {
          if (type === 'state') setState(msg.state);
          if (type === 'health') { setBitrateKbps(msg.bitrateKbps); setReaders(msg.readers); }
          if (type === 'telemetry' && msg.payload) {
            setTele({ speedKmh: msg.payload.speedKmh ?? null });
          }
        },
      },
    });
    return () => conn.close();
  }, [stream.id]);

  const patch = async (body) => {
    try {
      await RC.apiFetch('/api/streams/' + stream.id, { method: 'PATCH', body });
      await onChange();
    } catch (e) { toast?.(e.message, 'error'); }
  };
  const rotateKey = async () => {
    if (!confirm('Stream-Key rotieren? Aktive Publisher werden rausgeworfen.')) return;
    try {
      await RC.apiFetch('/api/streams/' + stream.id + '/rotate-key', { method: 'POST' });
      await onChange();
      toast?.('Stream-Key rotiert');
    } catch (e) { toast?.(e.message, 'error'); }
  };
  const kick = async () => {
    try {
      await RC.apiFetch('/api/streams/' + stream.id + '/kick', { method: 'POST' });
      toast?.('Publisher gekickt');
    } catch (e) { toast?.(e.message, 'error'); }
  };
  const endBroadcast = async () => {
    const bc = stream.currentBroadcast; if (!bc) return;
    if (!confirm('Broadcast jetzt beenden? Publisher wird gekickt.')) return;
    try {
      await RC.apiFetch(`/api/broadcasts/${bc.id}/end`, { method: 'POST' });
      toast?.('Broadcast beendet');
      await onChange();
    } catch (e) { toast?.(e.message, 'error'); }
  };

  const bc = stream.currentBroadcast;
  const dur = bc ? Math.max(0, Math.floor(Date.now() / 1000) - bc.startedAt) : 0;

  return (
    <div className="ed-side-grid">
      <div>
        {/* Live preview / offline curtain */}
        <LivePreview stream={stream} state={state} tele={tele} />

        {/* Broadcast hero card */}
        <div className="ed-broadcast-card">
          <div>
            <div className="ed-page-head__lead">
              {bc ? (bc.status === 'live' ? 'Now broadcasting' : 'Pending classification')
                  : 'Awaiting signal'}
            </div>
            <h2 className="ed-broadcast-card__title">
              {bc?.name ? bc.name : <em>{stream.name}</em>}
            </h2>
            <div className="ed-broadcast-card__sub">
              {bc?.location && <span>{bc.location}</span>}
              {bc?.location && <span style={{ margin: '0 10px', color: 'var(--ed-cream-trace)' }}>·</span>}
              <span>started {bc ? formatRelative(bc.startedAt) : '—'}</span>
            </div>
            <div className="ed-broadcast-card__chips">
              <span className={'ed-pill ed-pill--' + (state === 'live' ? 'live' : 'offline')}>
                {state === 'live' ? 'on air' : 'silent'}
              </span>
              {bc && <span className={'ed-pill ed-pill--' + bc.visibility}>{bc.visibility}</span>}
              {bc && bc.status === 'pending' && <span className="ed-pill ed-pill--pending">pending</span>}
              {bc && bc.status === 'ended'   && <span className="ed-pill ed-pill--ended">ended</span>}
            </div>
          </div>

          <div style={{ display: 'flex', gap: 16, alignItems: 'flex-end' }}>
            <div className="ed-broadcast-stat">
              <span className="ed-broadcast-stat__num">{formatDuration(dur)}</span>
              <span className="ed-broadcast-stat__unit">duration</span>
            </div>
          </div>
        </div>

        <div style={{ display: 'flex', gap: 8, padding: '20px 0', flexWrap: 'wrap' }}>
          {bc && bc.status !== 'ended' && (
            <button className="ed-btn ed-btn--ferrari" onClick={endBroadcast}>■ end broadcast</button>
          )}
          {bc && <button className="ed-btn" onClick={() => setEditingBroadcast(true)}>edit broadcast</button>}
          {state === 'live' && <button className="ed-btn ed-btn--ghost" onClick={kick}>kick publisher</button>}
        </div>

        {/* Live KPIs */}
        <div className="ed-strip-row" style={{ marginTop: 16 }}>
          <Cell label="Bitrate"  value={state === 'live' && bitrateKbps != null ? `${(bitrateKbps / 1000).toFixed(2)} Mbps` : '—'} />
          <Cell label="Audience" value={state === 'live' ? `${readers}` : '—'} />
          <Cell label="Speed"    value={state === 'live' && tele.speedKmh != null ? `${Math.round(tele.speedKmh)} km/h` : '—'} />
          <Cell label="Channel"  value={(bc?.visibility || '—').toUpperCase()} />
        </div>

        {/* Endpoints disclosure */}
        <Disclosure title="Endpoints" open={showEndpoints} onToggle={() => setShowEndpoints((v) => !v)}>
          <div className="ed-aside-section__title">Ingest · publisher pushes here</div>
          <UrlRow label="RTMP" url={stream.ingest.rtmp} toast={toast} />
          <UrlRow label="SRT"  url={stream.ingest.srt}  toast={toast} />
          <UrlRow label="RTSP" url={stream.ingest.rtsp} toast={toast} />
          <div style={{ display: 'flex', gap: 8, marginTop: 14, marginBottom: 24 }}>
            <button className="ed-btn ed-btn--ghost" onClick={rotateKey}>↻ rotate stream key</button>
          </div>
          <div className="ed-aside-section__title">Playback · viewers consume here</div>
          <UrlRow label="HLS"    url={stream.playback.hls}    toast={toast} />
          <UrlRow label="WebRTC" url={stream.playback.webrtc} toast={toast} />
          <ViewerLinkHint stream={stream} />
        </Disclosure>

        {/* Settings disclosure */}
        <Disclosure title="Settings" open={showSettings} onToggle={() => setShowSettings((v) => !v)}>
          <div className="ed-aside-section__title">Codec</div>
          <div style={{ display: 'flex', gap: 8 }}>
            {['h264', 'h265'].map((c) => (
              <button key={c}
                      className={'ed-choice'}
                      aria-selected={stream.codec === c}
                      onClick={() => patch({ codec: c })}
                      style={{ flex: 1 }}>
                <span className="ed-choice__title">{c.toUpperCase()}</span>
                <span className="ed-choice__sub">{c === 'h265' ? 'better compression · SRT/RTSP only' : 'works everywhere'}</span>
              </button>
            ))}
          </div>
          <div style={{ marginTop: 22 }}>
            <Toggle label="Recording" sub="MediaMTX writes fMP4 segments to disk"
                    value={stream.recordingEnabled}
                    onChange={(v) => patch({ recordingEnabled: v })} />
          </div>
        </Disclosure>
      </div>

      {/* Right column: editorial spec sheet */}
      <aside>
        <div className="ed-aside-section">
          <div className="ed-aside-section__title">The Channel</div>
          <div className="ed-kv"><span className="ed-kv__k">Identity</span><span className="ed-kv__v">{stream.name}</span></div>
          {stream.location && <div className="ed-kv"><span className="ed-kv__k">Origin</span><span className="ed-kv__v">{stream.location}</span></div>}
          <div className="ed-kv"><span className="ed-kv__k">Codec</span><span className="ed-kv__v">{stream.codec.toUpperCase()}</span></div>
          <div className="ed-kv"><span className="ed-kv__k">Recording</span><span className="ed-kv__v">{stream.recordingEnabled ? 'enabled' : 'disabled'}</span></div>
          <div className="ed-kv"><span className="ed-kv__k">Slug</span><span className="ed-kv__v ed-mono">{stream.slug}</span></div>
        </div>

        {bc && (
          <div className="ed-aside-section">
            <div className="ed-aside-section__title">Current Broadcast</div>
            <div className="ed-kv"><span className="ed-kv__k">Status</span><span className="ed-kv__v">{bc.status}</span></div>
            <div className="ed-kv"><span className="ed-kv__k">Visibility</span><span className="ed-kv__v">{bc.visibility}</span></div>
            {bc.viewerPassword && <div className="ed-kv"><span className="ed-kv__k">Password</span><span className="ed-kv__v ed-mono">{bc.viewerPassword}</span></div>}
            <div className="ed-kv"><span className="ed-kv__k">Started</span><span className="ed-kv__v">{formatRelative(bc.startedAt)}</span></div>
            <div className="ed-kv"><span className="ed-kv__k">Last Seen</span><span className="ed-kv__v">{formatRelative(bc.lastSeenAt)}</span></div>
          </div>
        )}
      </aside>

      {editingBroadcast && bc && <ClassifyBroadcastModal
        broadcast={bc}
        title="Edit broadcast"
        onClose={() => setEditingBroadcast(false)}
        onSaved={async () => { setEditingBroadcast(false); await onChange(); }}
        toast={toast} />}
    </div>
  );
}

function LivePreview({ stream, state, tele }) {
  const videoRef = useRA(null);
  const hlsRef = useRA(null);

  useEA(() => {
    const v = videoRef.current; if (!v) return;
    if (state !== 'live') {
      try { hlsRef.current?.destroy(); } catch (_) {}
      hlsRef.current = null;
      v.removeAttribute('src'); v.load?.();
      return;
    }
    const url = stream.playback.hls;
    if (v.canPlayType('application/vnd.apple.mpegurl')) {
      v.src = url; v.play().catch(() => {});
    } else if (window.Hls && Hls.isSupported()) {
      const hls = new Hls({ lowLatencyMode: true, liveSyncDuration: 2 });
      hls.loadSource(url); hls.attachMedia(v);
      hlsRef.current = hls; v.play().catch(() => {});
    }
    return () => { try { hlsRef.current?.destroy(); } catch (_) {} hlsRef.current = null; };
  }, [stream.playback.hls, state]);

  return (
    <div className="ed-stage" style={{ aspectRatio: '16 / 9', position: 'relative' }}>
      <video ref={videoRef} muted playsInline controls={false}
             style={{ width: '100%', height: '100%', objectFit: 'contain', background: '#000' }} />
      {state !== 'live' && (
        <div style={{
          position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column',
          alignItems: 'center', justifyContent: 'center', gap: 14,
          color: 'var(--ed-cream-faint)',
        }}>
          <div className="ed-card__lead" style={{ marginBottom: 0 }}>Awaiting signal</div>
          <span style={{ fontFamily: 'var(--ed-body)', fontStyle: 'italic', fontSize: 13 }}>
            push to <span className="ed-mono" style={{ color: 'var(--ed-cream-dim)' }}>
              {stream.ingest.rtmp.split('/').slice(0, -1).join('/')}/{'<key>'}
            </span>
          </span>
        </div>
      )}
      {state === 'live' && tele.speedKmh != null && (
        <div style={{ position: 'absolute', bottom: 16, left: 16, padding: '6px 12px',
                      background: 'rgba(13,12,10,0.78)', border: '1px solid rgba(237,229,207,0.18)',
                      fontFamily: 'var(--ed-mono)', fontSize: 11, color: 'var(--ed-cream)' }}>
          {Math.round(tele.speedKmh)} km/h
        </div>
      )}
    </div>
  );
}

function UrlRow({ label, url, toast }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '8px 0' }}>
      <span style={{ width: 70, fontFamily: 'var(--ed-body)', fontStyle: 'italic',
                     fontSize: 11, color: 'var(--ed-cream-faint)',
                     letterSpacing: '0.18em', textTransform: 'uppercase' }}>{label}</span>
      <div className="ed-url" style={{ flex: 1 }}>
        <span className="ed-url__code">{url}</span>
        <button className="ed-url__copy"
                onClick={() => { RC.copy(url); toast?.('copied'); }}>
          copy
        </button>
      </div>
    </div>
  );
}

function ViewerLinkHint({ stream }) {
  const bc = stream.currentBroadcast;
  const link = `/?view=${stream.slug}`;
  if (!bc || bc.status !== 'live') {
    return <div style={{ fontFamily: 'var(--ed-body)', fontStyle: 'italic',
                         fontSize: 12, color: 'var(--ed-cream-faint)', marginTop: 12 }}>
      no public viewer link until a broadcast is live.
    </div>;
  }
  return <div style={{ fontFamily: 'var(--ed-body)', fontStyle: 'italic',
                       fontSize: 12, color: bc.visibility === 'private' ? 'var(--ed-amber)' : 'var(--ed-cream-dim)',
                       marginTop: 12 }}>
    {bc.visibility === 'private' ? 'private — viewers need the password at ' : 'public viewer link · '}
    <a href={link} style={{ color: 'var(--ed-ferrari)' }}>{link}</a>
  </div>;
}

function Disclosure({ title, open, onToggle, children }) {
  return (
    <div className="ed-disclosure">
      <div className="ed-disclosure__head" onClick={onToggle}>
        <span className="ed-disclosure__title">{title}</span>
        <span className="ed-disclosure__caret">{open ? '— close' : '+ open'}</span>
      </div>
      {open && <div style={{ paddingBottom: 18 }}>{children}</div>}
    </div>
  );
}

function Cell({ label, value }) {
  return (
    <div className="ed-strip-cell">
      <span className="ed-strip-cell__label">{label}</span>
      <span className="ed-strip-cell__value">{value}</span>
    </div>
  );
}

function Toggle({ label, sub, value, onChange }) {
  return (
    <label className={'ed-toggle' + (value ? ' ed-toggle--on' : '')} onClick={(e) => e.preventDefault()}>
      <button type="button" className="ed-toggle__track" onClick={() => onChange(!value)}>
        <span className="ed-toggle__thumb" />
      </button>
      <span>
        <span className="ed-toggle__label">{label}</span>
        {sub && <span className="ed-toggle__sub">{sub}</span>}
      </span>
    </label>
  );
}

// ─── Sessions tab ──────────────────────────────────────────────────
function SessionsTab({ streams, toast }) {
  const [list, setList] = useSA([]);
  const [creating, setCreating] = useSA(false);
  const refresh = useCA(async () => {
    try { setList(await RC.apiFetch('/api/sessions')); }
    catch (e) { toast?.(e.message, 'error'); }
  }, []);
  useEA(() => { refresh(); }, []);

  const del = async (id) => {
    if (!confirm('Session löschen?')) return;
    await RC.apiFetch('/api/sessions/' + id, { method: 'DELETE' });
    refresh();
  };

  return (
    <>
      <div className="ed-page-head">
        <div>
          <div className="ed-page-head__lead">Schedule</div>
          <h1 className="ed-page-head__title">Race <em>sessions</em>.</h1>
        </div>
        <div className="ed-page-head__actions">
          <button className="ed-btn ed-btn--ferrari" onClick={() => setCreating(true)}>+ new session</button>
        </div>
      </div>

      {list.length === 0 ? (
        <EmptyList title="No sessions yet."
          body="Sessions sind geplante Termine — sie helfen, Broadcasts dem richtigen Renn-Kontext zuzuordnen." />
      ) : (
        <div className="ed-list">
          {list.map((s) => {
            const stream = streams.find((x) => x.id === s.streamId);
            return (
              <div className="ed-list__row" key={s.id}>
                <div>
                  <div className="ed-list__title">{s.name}</div>
                  <div className="ed-list__sub">
                    {RC.fmtTimeAgo(s.startsAt)}
                    {stream && <> · <span style={{ color: 'var(--ed-cream-dim)' }}>{stream.name}</span></>}
                  </div>
                </div>
                <div />
                <span className={'ed-pill ' + (s.status === 'live' ? 'ed-pill--live' : 'ed-pill--pending')}>{s.status}</span>
                <div className="ed-list__actions">
                  <button className="ed-btn ed-btn--ghost" onClick={() => del(s.id)}>delete</button>
                </div>
              </div>
            );
          })}
        </div>
      )}
      {creating && <CreateSessionModal streams={streams} onClose={() => setCreating(false)}
        onCreated={() => { setCreating(false); refresh(); }} toast={toast} />}
    </>
  );
}

function CreateSessionModal({ streams, onClose, onCreated, toast }) {
  const [name, setName] = useSA('');
  const [streamId, setStreamId] = useSA(streams[0]?.id || '');
  const [when, setWhen] = useSA(() => new Date().toISOString().slice(0, 16));
  const [busy, setBusy] = useSA(false);
  const submit = async (e) => {
    e.preventDefault(); setBusy(true);
    try {
      await RC.apiFetch('/api/sessions', {
        method: 'POST',
        body: { name, streamId, startsAt: Math.floor(new Date(when).getTime() / 1000) },
      });
      onCreated();
    } catch (err) { toast?.(err.message, 'error'); }
    finally { setBusy(false); }
  };
  return (
    <Modal lead="Schedule" title={<>A new <em>session</em>.</>} onClose={onClose}>
      <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        <FormField label="Name">
          <input className="ed-input" value={name} onChange={(e) => setName(e.target.value)} required autoFocus />
        </FormField>
        <FormField label="Stream">
          <select className="ed-input" value={streamId} onChange={(e) => setStreamId(e.target.value)}>
            {streams.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
          </select>
        </FormField>
        <FormField label="Starts">
          <input className="ed-input" type="datetime-local" value={when} onChange={(e) => setWhen(e.target.value)} />
        </FormField>
        <ModalFoot>
          <button type="button" className="ed-btn" onClick={onClose}>cancel</button>
          <button type="submit" className="ed-btn ed-btn--ferrari" disabled={busy}>
            {busy ? <span className="ed-spin" /> : 'create session ›'}
          </button>
        </ModalFoot>
      </form>
    </Modal>
  );
}

// ─── Replays tab ──────────────────────────────────────────────────
function ReplaysTab({ stream, toast }) {
  const [list, setList] = useSA([]);
  const [editing, setEditing] = useSA(null);
  const refresh = useCA(async () => {
    try {
      const all = await RC.apiFetch('/api/replays');
      setList(all.filter((r) => r.streamId === stream.id));
    } catch (e) { toast?.(e.message, 'error'); }
  }, [stream.id]);
  useEA(() => { refresh(); }, [stream.id]);
  const del = async (id) => {
    if (!confirm('Recording löschen?')) return;
    await RC.apiFetch('/api/replays/' + id, { method: 'DELETE' });
    refresh();
  };

  return (
    <>
      <div className="ed-page-head">
        <div>
          <div className="ed-page-head__lead">Archive · {stream.name}</div>
          <h1 className="ed-page-head__title">The <em>replays</em>.</h1>
        </div>
      </div>

      {list.length === 0 ? (
        <EmptyList title="No recordings yet."
          body={<>MediaMTX zeichnet automatisch jedes Push-Segment auf. Sobald die Cam zu <span className="ed-mono" style={{ color: 'var(--ed-cream-dim)' }}>{stream.name}</span> pusht, erscheinen die Mitschnitte hier.</>} />
      ) : (
        <div className="ed-list">
          {list.map((r) => (
            <div className="ed-list__row" key={r.id}>
              <div>
                <div className="ed-list__title">{RC.fmtTimeAgo(r.startedAt)}</div>
                <div className="ed-list__sub">
                  {r.codec?.toUpperCase()} · {r.width}×{r.height} · {RC.fmtBytes(r.sizeBytes)}
                </div>
              </div>
              <span className="ed-list__meta">{RC.fmtDuration(r.durationSeconds)}</span>
              <span style={{ fontFamily: 'var(--ed-body)', fontStyle: 'italic',
                             fontSize: 11, color: 'var(--ed-cream-faint)' }}>
                {r.views} {r.views === 1 ? 'play' : 'plays'}
              </span>
              <div className="ed-list__actions">
                <button className="ed-btn ed-btn--ghost" onClick={() => setEditing(r)}>✂ clip</button>
                <button className="ed-btn ed-btn--ghost" onClick={() => del(r.id)}>delete</button>
              </div>
            </div>
          ))}
        </div>
      )}

      {editing && <ClipEditor recording={editing} toast={toast} onClose={() => setEditing(null)} />}
    </>
  );
}

// ─── Clips tab ────────────────────────────────────────────────────
function ClipsTab({ stream, toast }) {
  const [list, setList] = useSA([]);
  const refresh = useCA(async () => {
    try {
      const all = await RC.apiFetch('/api/clips');
      setList(all.filter((c) => c.streamId === stream.id));
    } catch (e) { toast?.(e.message, 'error'); }
  }, [stream.id]);
  useEA(() => { refresh(); }, [stream.id]);
  useEA(() => {
    if (!list.some((c) => c.status === 'pending')) return;
    const t = setInterval(refresh, 2000);
    return () => clearInterval(t);
  }, [list, refresh]);
  const del = async (id) => {
    if (!confirm('Clip löschen?')) return;
    await RC.apiFetch('/api/clips/' + id, { method: 'DELETE' });
    refresh();
  };

  return (
    <>
      <div className="ed-page-head">
        <div>
          <div className="ed-page-head__lead">Archive · {stream.name}</div>
          <h1 className="ed-page-head__title">The <em>clips</em>.</h1>
        </div>
      </div>

      {list.length === 0 ? (
        <EmptyList title="No clips yet."
          body="Aus jedem Replay lässt sich ein Clip schneiden. Im Replays-Tab auf ✂ klicken, Start- und Endpunkt setzen, fertig." />
      ) : (
        <div className="ed-list">
          {list.map((c) => (
            <div className="ed-list__row" key={c.id}>
              <div>
                <div className="ed-list__title">{c.name}</div>
                <div className="ed-list__sub ed-mono">
                  {RC.fmtDuration(c.startSeconds)} → {RC.fmtDuration(c.endSeconds)}
                </div>
              </div>
              <span className="ed-list__meta">{RC.fmtBytes(c.sizeBytes)}</span>
              <span className={'ed-pill ' + (c.status === 'ready' ? 'ed-pill--ready' : 'ed-pill--pending')}>{c.status}</span>
              <div className="ed-list__actions">
                {c.status === 'ready' && <a className="ed-btn ed-btn--ghost"
                  href={`${RC.baseUrl}/api/clips/${c.id}/file?token=${RC.getToken()}`}
                  target="_blank" rel="noreferrer" style={{ textDecoration: 'none' }}>↓ download</a>}
                <button className="ed-btn ed-btn--ghost" onClick={() => del(c.id)}>delete</button>
              </div>
            </div>
          ))}
        </div>
      )}
    </>
  );
}

// ─── Overlay tab ──────────────────────────────────────────────────
function OverlayTab({ stream, toast }) {
  const [layout, setLayout] = useSA({ logo: true, speed: true, avgSpeed: true, dataRate: false });
  const [loaded, setLoaded] = useSA(false);

  useEA(() => {
    let cancelled = false;
    (async () => {
      try {
        const list = await RC.apiFetch('/api/overlays/' + stream.id);
        const def = list.find((o) => o.name === 'default') || list[0];
        if (!cancelled && def) setLayout({ ...layout, ...def.layout });
        if (!cancelled) setLoaded(true);
      } catch (e) { toast?.(e.message, 'error'); }
    })();
    return () => { cancelled = true; };
  }, [stream.id]);

  const save = async () => {
    try {
      await RC.apiFetch('/api/overlays/' + stream.id + '/default', { method: 'PUT', body: { layout } });
      toast?.('Overlay gespeichert');
    } catch (e) { toast?.(e.message, 'error'); }
  };

  return (
    <>
      <div className="ed-page-head">
        <div>
          <div className="ed-page-head__lead">Editor</div>
          <h1 className="ed-page-head__title">Overlay <em>layout</em>.</h1>
        </div>
        <div className="ed-page-head__actions">
          <button className="ed-btn ed-btn--ferrari" onClick={save} disabled={!loaded}>save layout</button>
        </div>
      </div>

      <div className="ed-side-grid">
        <div style={{ position: 'relative', aspectRatio: '16/9', background: '#101216',
                      border: '1px solid var(--ed-rule)' }}>
          <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center',
                        justifyContent: 'center', color: 'var(--ed-cream-faint)',
                        fontFamily: 'var(--ed-body)', fontStyle: 'italic' }}>preview</div>
          {layout.logo && <div style={{ position: 'absolute', top: 14, left: 14,
                                        background: 'rgba(13,12,10,0.78)', padding: '5px 10px',
                                        border: '1px dashed var(--ed-ferrari)',
                                        fontFamily: 'var(--ed-serif)', fontSize: 13,
                                        fontStyle: 'italic', color: 'var(--ed-cream)' }}>
            RaceCast
          </div>}
          {layout.speed && <div style={{ position: 'absolute', bottom: 16, left: 16,
            background: 'rgba(220,31,31,0.95)', padding: '8px 14px',
            border: '1px dashed rgba(255,255,255,0.5)' }}>
            <div style={{ fontSize: 9, color: 'rgba(255,255,255,0.8)', letterSpacing: '0.18em', textTransform: 'uppercase' }}>SPEED</div>
            <div style={{ fontFamily: 'var(--ed-serif)', fontSize: 36, color: '#fff', lineHeight: 1 }}>247<span style={{ fontSize: 12, opacity: 0.7, marginLeft: 4 }}>km/h</span></div>
          </div>}
          {layout.avgSpeed && <div style={{ position: 'absolute', bottom: 16, left: 200,
            background: 'rgba(13,12,10,0.78)', border: '1px dashed var(--ed-amber)',
            padding: '8px 14px' }}>
            <div style={{ fontSize: 9, color: 'var(--ed-cream-faint)', letterSpacing: '0.18em' }}>Ø SPEED</div>
            <div style={{ fontFamily: 'var(--ed-serif)', fontSize: 22, color: 'var(--ed-cream)' }}>186</div>
          </div>}
          {layout.dataRate && <div style={{ position: 'absolute', top: 14, right: 14,
            background: 'rgba(13,12,10,0.78)', border: '1px dashed var(--ed-cream-dim)',
            padding: '6px 12px', fontFamily: 'var(--ed-mono)', fontSize: 11, color: 'var(--ed-cream)' }}>
            ● 6.5 Mbps
          </div>}
        </div>

        <aside>
          <div className="ed-aside-section">
            <div className="ed-aside-section__title">Visible elements</div>
            <Toggle label="Team logo" value={layout.logo} onChange={(v) => setLayout({ ...layout, logo: v })} />
            <div style={{ height: 6 }} />
            <Toggle label="Speed badge" value={layout.speed} onChange={(v) => setLayout({ ...layout, speed: v })} />
            <div style={{ height: 6 }} />
            <Toggle label="Average speed" value={layout.avgSpeed} onChange={(v) => setLayout({ ...layout, avgSpeed: v })} />
            <div style={{ height: 6 }} />
            <Toggle label="Data rate" value={layout.dataRate} onChange={(v) => setLayout({ ...layout, dataRate: v })} />
          </div>
        </aside>
      </div>
    </>
  );
}

// ─── Settings tab ──────────────────────────────────────────────────
function SettingsTab({ toast, onTeamUpdated }) {
  const [data, setData] = useSA(null);
  const [teamName, setTeamName] = useSA('');
  const [accent, setAccent] = useSA('#ff2b2b');

  useEA(() => {
    RC.apiFetch('/api/settings').then((d) => {
      setData(d); setTeamName(d.team.name); setAccent(d.settings.accent_color);
    }).catch((e) => toast?.(e.message, 'error'));
  }, []);

  const save = async () => {
    try {
      const d = await RC.apiFetch('/api/settings', {
        method: 'PATCH', body: { teamName, accentColor: accent },
      });
      setData(d); toast?.('saved');
      onTeamUpdated?.();
    } catch (e) { toast?.(e.message, 'error'); }
  };

  if (!data) return <div className="ed-loader" style={{ margin: 80 }} />;

  return (
    <>
      <div className="ed-page-head">
        <div>
          <div className="ed-page-head__lead">Account</div>
          <h1 className="ed-page-head__title">Team <em>settings</em>.</h1>
        </div>
        <div className="ed-page-head__actions">
          <button className="ed-btn ed-btn--ferrari" onClick={save}>save</button>
        </div>
      </div>

      <div style={{ maxWidth: 640 }}>
        <div className="ed-aside-section">
          <div className="ed-aside-section__title">Team</div>
          <FormField label="Name">
            <input className="ed-input" value={teamName} onChange={(e) => setTeamName(e.target.value)} />
          </FormField>
        </div>
        <div className="ed-aside-section">
          <div className="ed-aside-section__title">Encoding defaults</div>
          <div className="ed-detail">
            <div className="ed-detail__label">Resolution</div>
            <div className="ed-detail__value ed-mono">{data.settings.default_resolution}</div>
            <div className="ed-detail__label">Framerate</div>
            <div className="ed-detail__value ed-mono">{data.settings.default_framerate} fps</div>
            <div className="ed-detail__label">Codec</div>
            <div className="ed-detail__value ed-mono">{data.settings.default_codec.toUpperCase()}</div>
            <div className="ed-detail__label">Bitrate</div>
            <div className="ed-detail__value ed-mono">{(data.settings.default_bitrate_kbps / 1000).toFixed(1)} Mbps</div>
          </div>
        </div>
      </div>
    </>
  );
}

// ─── Create-stream modal ───────────────────────────────────────────
function CreateStreamModal({ onClose, onCreated, toast }) {
  const [name, setName] = useSA('');
  const [location, setLocation] = useSA('');
  const [busy, setBusy] = useSA(false);

  const valid = name.trim().length > 0;
  const submit = async (e) => {
    e.preventDefault(); if (!valid) return;
    setBusy(true);
    try {
      const created = await RC.apiFetch('/api/streams', {
        method: 'POST',
        body: {
          name: name.trim(),
          location: location.trim() || null,
          codec: 'h264',
          // Channel-level visibility doesn't matter — broadcasts get
          // classified individually. Pass 'unlisted' as the legacy field.
          visibility: 'unlisted',
        },
      });
      toast?.(`Stream "${created.name}" erstellt`);
      await onCreated(created);
    } catch (err) { toast?.(err.message, 'error'); }
    finally { setBusy(false); }
  };

  return (
    <Modal lead="New channel" title={<>A new <em>stream</em>.</>} onClose={onClose}>
      <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
        <FormField label="Name">
          <input className="ed-input" value={name} onChange={(e) => setName(e.target.value)}
                 placeholder="e.g. Onboard Cam #7" required autoFocus />
        </FormField>
        <FormField label="Origin">
          <input className="ed-input" value={location} onChange={(e) => setLocation(e.target.value)}
                 placeholder="e.g. Hockenheim" />
        </FormField>
        <p style={{ fontFamily: 'var(--ed-body)', fontStyle: 'italic',
                    fontSize: 12, color: 'var(--ed-cream-faint)', lineHeight: 1.5 }}>
          Each push to this stream creates a fresh, classifiable broadcast. The
          stream-key is generated automatically and is the cam's push password.
        </p>
        <ModalFoot>
          <button type="button" className="ed-btn" onClick={onClose}>cancel</button>
          <button type="submit" className="ed-btn ed-btn--ferrari" disabled={busy || !valid}>
            {busy ? <span className="ed-spin" /> : 'create stream ›'}
          </button>
        </ModalFoot>
      </form>
    </Modal>
  );
}

// ─── shared bits ───────────────────────────────────────────────────
function Modal({ lead, title, onClose, wide, children }) {
  return (
    <div className="ed-modal-backdrop" onClick={onClose}>
      <div className={'ed-modal' + (wide ? ' ed-modal--wide' : '')} onClick={(e) => e.stopPropagation()}>
        <div className="ed-modal__head">
          <div>
            {lead && <div className="ed-modal__lead">{lead}</div>}
            <h2 className="ed-modal__title">{title}</h2>
          </div>
          <button className="ed-modal__close" onClick={onClose}>✕</button>
        </div>
        <div className="ed-modal__body">{children}</div>
      </div>
    </div>
  );
}
function ModalFoot({ children }) {
  return <div className="ed-modal__foot">{children}</div>;
}
function FormField({ label, children }) {
  return (
    <label className="ed-form-row">
      <span className="ed-form-row__label">{label}</span>
      {children}
    </label>
  );
}
function EmptyList({ title, body }) {
  return (
    <div className="ed-list__empty">
      <div className="ed-list__empty-lead">·  ·  ·</div>
      <div style={{ fontFamily: 'var(--ed-serif)',
                    fontVariationSettings: "'opsz' 64, 'SOFT' 50",
                    fontWeight: 320, fontSize: 32, color: 'var(--ed-cream)',
                    letterSpacing: '-0.02em', marginBottom: 14 }}>{title}</div>
      <div className="ed-list__empty-text">{body}</div>
    </div>
  );
}

function formatDuration(sec) {
  if (!sec) return '0:00';
  const h = Math.floor(sec / 3600);
  const m = Math.floor((sec / 60) % 60).toString().padStart(2, '0');
  const s = (sec % 60).toString().padStart(2, '0');
  return h > 0 ? `${h}:${m}:${s}` : `${m}:${s}`;
}
function formatRelative(ts) {
  if (!ts) return '—';
  const diff = Math.floor(Date.now() / 1000) - ts;
  if (diff < 60) return `${diff}s ago`;
  if (diff < 3600) return `${Math.floor(diff / 60)} min ago`;
  if (diff < 86400) return `${Math.floor(diff / 3600)} h ago`;
  return new Date(ts * 1000).toLocaleDateString('de-DE', { day: '2-digit', month: 'short' });
}

window.LiveAdmin = LiveAdmin;
