/* global React, RC, RCSpinner, Hls */
// Public viewer — Editorial Motorsport aesthetic.
// Italian race-photography annual energy: oxidised cream on warm ink,
// sweeping Fraunces serif numerals as the hero, hairline rules,
// restrained ferrari-red accents. Print magazine, not dashboard.
//
// Stage states:
//   'loading'   → tuning frequencies (hairline loader)
//   'password'  → editorial card with single underline input
//   'error'     → "OFF THE RECORD" 404
//   'ready'     → BroadcastTheatre

const { useState: useSV, useEffect: useEV, useRef: useRefV, useMemo: useMV } = React;

// ── HLS plumbing (unchanged) ────────────────────────────────────────
function useHlsPlayer(srcUrl, { live = true } = {}) {
  const videoRef = useRefV(null);
  const [error, setError] = useSV(null);
  useEV(() => {
    setError(null);
    const video = videoRef.current;
    if (!video || !srcUrl) return;
    let hls;
    if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = srcUrl;
      const onErr = () => setError('Playback fehlgeschlagen');
      video.addEventListener('error', onErr);
      video.play().catch(() => {});
      return () => video.removeEventListener('error', onErr);
    }
    if (window.Hls && Hls.isSupported()) {
      hls = new Hls({
        lowLatencyMode: live, liveSyncDuration: 2,
        maxLiveSyncPlaybackRate: 1.5,
        backBufferLength: live ? 30 : 60,
      });
      hls.loadSource(srcUrl); hls.attachMedia(video);
      hls.on(Hls.Events.ERROR, (_e, data) => {
        if (!data.fatal) return;
        if (data.type === Hls.ErrorTypes.NETWORK_ERROR) setError('Stream nicht verfügbar');
        else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) {
          try { hls.recoverMediaError(); } catch (_) {}
        } else setError('Player-Fehler: ' + data.details);
      });
      video.play().catch(() => {});
      return () => { try { hls.destroy(); } catch (_) {} };
    }
    setError('Browser unterstützt HLS nicht');
  }, [srcUrl, live]);
  return { videoRef, error };
}

// ── Top-level router for viewer states ─────────────────────────────
function LiveViewer({ slug, toast }) {
  const [stage, setStage] = useSV('loading');
  const [info, setInfo] = useSV(null);
  const [pw, setPw] = useSV('');
  const [submitting, setSubmitting] = useSV(false);

  useEV(() => {
    let cancelled = false;
    (async () => {
      try {
        const data = await RC.viewerLogin(slug, '');
        if (cancelled) return;
        setInfo(data);
        setStage('ready');
      } catch (e) {
        if (cancelled) return;
        if (e.status === 401) setStage('password');
        else setStage('error');
      }
    })();
    return () => { cancelled = true; };
  }, [slug]);

  const submitPw = async (e) => {
    e?.preventDefault?.();
    setSubmitting(true);
    try {
      const data = await RC.viewerLogin(slug, pw);
      setInfo(data);
      setStage('ready');
    } catch (err) {
      toast?.('Falsches Stream-Passwort', 'error');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="ed-root" style={{ minHeight: '100%', position: 'relative' }}>
      <div className="ed-grain" aria-hidden="true" />
      <div className="ed-vignette" aria-hidden="true" />
      {stage === 'loading'  && <LoadingState />}
      {stage === 'error'    && <ErrorState slug={slug} />}
      {stage === 'password' && <PasswordState slug={slug} pw={pw} setPw={setPw}
                                              submitting={submitting} onSubmit={submitPw} />}
      {stage === 'ready' && info && <BroadcastTheatre info={info} toast={toast} />}
    </div>
  );
}

// ── Loading ─────────────────────────────────────────────────────────
function LoadingState() {
  return (
    <div className="ed-page">
      <div className="ed-centered">
        <div style={{ textAlign: 'center' }}>
          <div className="ed-card__lead" style={{ marginBottom: 24 }}>tuning frequencies</div>
          <div className="ed-loader" style={{ margin: '0 auto' }} />
        </div>
      </div>
    </div>
  );
}

// ── 404 ─────────────────────────────────────────────────────────────
function ErrorState({ slug }) {
  return (
    <div className="ed-page">
      <div className="ed-centered">
        <div className="ed-card">
          <div className="ed-card__lead">Off the record</div>
          <h1 className="ed-card__title">
            No <em>signal</em> on this frequency.
          </h1>
          <p className="ed-card__body">
            Der Slug <span className="ed-mono" style={{ color: 'var(--ed-cream)' }}>{slug}</span> ist nicht
            (mehr) auf Sendung. Vielleicht wurde der Broadcast beendet, oder
            das Rennen ist noch nicht freigegeben.
          </p>
          <a href="/" className="ed-btn ed-btn--ghost" style={{ textDecoration: 'none' }}>← Zurück</a>
        </div>
      </div>
    </div>
  );
}

// ── Password gate ───────────────────────────────────────────────────
function PasswordState({ slug, pw, setPw, submitting, onSubmit }) {
  return (
    <div className="ed-page">
      <div className="ed-centered">
        <form onSubmit={onSubmit} className="ed-card" style={{ maxWidth: 520 }}>
          <div className="ed-card__lead">Access required</div>
          <h1 className="ed-card__title">
            A <em>private</em> session.
          </h1>
          <p className="ed-card__body">
            Dieser Stream ist nur mit Passwort zugänglich.
          </p>
          <label style={{ display: 'block', marginBottom: 28 }}>
            <span className="ed-label" style={{ display: 'block', marginBottom: 6 }}>Passwort</span>
            <input
              className="ed-input"
              type="password"
              value={pw}
              onChange={(e) => setPw(e.target.value)}
              placeholder="enter the season"
              autoFocus
              required
            />
          </label>
          <div style={{ display: 'flex', gap: 12, alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontFamily: 'var(--ed-mono)', fontSize: 11, color: 'var(--ed-cream-faint)' }}>
              {slug}
            </span>
            <button type="submit" className="ed-btn ed-btn--ferrari" disabled={submitting}>
              {submitting ? 'verifying…' : 'enter ›'}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

// ── The actual broadcast theatre ────────────────────────────────────
function BroadcastTheatre({ info, toast }) {
  const [state, setState] = useSV(info.state || 'offline');
  const [tele, setTele] = useSV({ speedKmh: null, avgSpeed: null, history: [] });
  const [bitrateKbps, setBitrateKbps] = useSV(null);
  const [readers, setReaders] = useSV(null);
  const [muted, setMuted] = useSV(true);
  const startedAt = useMV(() => Date.now(), []);
  const [now, setNow] = useSV(Date.now());

  const { videoRef, error } = useHlsPlayer(info.playback.hls);

  // tick-tick clock for the masthead
  useEV(() => {
    const t = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(t);
  }, []);

  // WS subscribe
  useEV(() => {
    const conn = RC.openWs({
      token: RC.getViewerToken(),
      handlers: {
        onOpen: () => conn.subscribe(info.streamId),
        onMessage: (type, msg) => {
          if (type === 'state') setState(msg.state);
          if (type === 'health') {
            setBitrateKbps(msg.bitrateKbps);
            if (msg.readers != null) setReaders(msg.readers);
          }
          if (type === 'telemetry' && msg.payload) {
            setTele((t) => {
              const v = msg.payload.speedKmh;
              const hist = v != null ? [...t.history.slice(-59), v] : t.history;
              const avg = hist.length ? hist.reduce((a, b) => a + b, 0) / hist.length : null;
              return { speedKmh: v ?? t.speedKmh, avgSpeed: avg, history: hist };
            });
          }
        },
      },
    });
    return () => conn.close();
  }, [info.streamId]);

  const elapsed = Math.max(0, Math.floor((now - startedAt) / 1000));
  const clock = formatClock(elapsed);

  return (
    <div className="ed-page">
      <div className="ed-shell">
        <Masthead title={info.name} state={state} clock={clock} />
        <hr className="ed-hr ed-hr--strong" />

        <main style={{ padding: '32px 0 24px', flex: 1, display: 'flex', flexDirection: 'column', gap: 28 }}>
          <Stage videoRef={videoRef} state={state} error={error}
                 muted={muted} setMuted={setMuted}
                 streamName={info.name} clock={clock} />

          <VelocityHero state={state} speed={tele.speedKmh} avg={tele.avgSpeed} />

          <TelemetryStrip
            state={state}
            bitrateKbps={bitrateKbps}
            readers={readers}
            visibility={info.visibility}
            history={tele.history}
          />
        </main>

        <Footer />
      </div>
    </div>
  );
}

// ── Masthead ────────────────────────────────────────────────────────
function Masthead({ title, state, clock }) {
  const today = new Date();
  const dateLabel = today.toLocaleDateString('de-DE', { day: '2-digit', month: 'long', year: 'numeric' });
  return (
    <header className="ed-masthead">
      <div className="ed-masthead__logo">
        <span style={{ fontStyle: 'italic' }}>RaceCast</span>
        <span style={{
          fontFamily: 'var(--ed-body)', fontStyle: 'italic',
          fontSize: 12, letterSpacing: '0.2em', textTransform: 'uppercase',
          color: 'var(--ed-cream-faint)', marginLeft: 10,
        }}>
          Editoriale
        </span>
      </div>

      <div className="ed-masthead__center">
        <span style={{ color: 'var(--ed-cream)' }}>{title || 'Anonymous Run'}</span>
        <span style={{ margin: '0 14px', color: 'var(--ed-cream-trace)' }}>·</span>
        <span>{dateLabel}</span>
      </div>

      <div className="ed-masthead__right">
        {state === 'live' ? (
          <span className="ed-live">live · {clock}</span>
        ) : (
          <span className="ed-strip" style={{ color: 'var(--ed-cream-faint)' }}>
            paused · {clock}
          </span>
        )}
      </div>
    </header>
  );
}

// ── Stage ───────────────────────────────────────────────────────────
function Stage({ videoRef, state, error, muted, setMuted, streamName, clock }) {
  return (
    <div className="ed-stage" style={{ aspectRatio: '16 / 9', borderRadius: 0 }}>
      <video ref={videoRef} muted={muted} playsInline controls={false} />

      {state !== 'live' && (
        <OfflineCurtain message={error || 'Stream is currently dark'} />
      )}

      {state === 'live' && (
        <>
          <div className="ed-stage-meta">
            <span className="ed-live" style={{ fontSize: 10 }}>onboard · live</span>
          </div>
          <div className="ed-stage-foot">
            <span style={{ letterSpacing: '0.18em' }}>{(streamName || '').toUpperCase()}</span>
            <span>{clock}</span>
          </div>
        </>
      )}

      {/* mute toggle, lower-right, only on hover-able context */}
      <button
        onClick={() => setMuted((m) => !m)}
        title={muted ? 'unmute' : 'mute'}
        style={{
          position: 'absolute', right: 12, top: 12,
          width: 36, height: 36, borderRadius: 0,
          background: 'rgba(13, 12, 10, 0.7)',
          border: '1px solid rgba(237, 229, 207, 0.18)',
          color: 'var(--ed-cream-dim)',
          cursor: 'pointer',
          fontFamily: 'var(--ed-mono)', fontSize: 11,
        }}
      >{muted ? '✕' : '◊'}</button>
    </div>
  );
}

function OfflineCurtain({ message }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      gap: 16, color: 'var(--ed-cream-faint)', fontFamily: 'var(--ed-body)',
      background: 'radial-gradient(ellipse at center, rgba(13,12,10,0.4) 30%, rgba(0,0,0,0.85) 100%)',
    }}>
      <span className="ed-card__lead" style={{ marginBottom: 0 }}>
        Pas de signal
      </span>
      <span style={{ fontStyle: 'italic', fontSize: 14, letterSpacing: '0.04em' }}>{message}</span>
    </div>
  );
}

// ── Velocity hero — the magazine cover number ───────────────────────
function VelocityHero({ state, speed, avg }) {
  const v = state === 'live' && speed != null ? Math.round(speed) : null;
  const a = state === 'live' && avg != null ? Math.round(avg) : null;
  return (
    <section className="ed-hero">
      <div>
        <div className="ed-label" style={{ marginBottom: 14 }}>
          <span style={{ color: 'var(--ed-ferrari)', marginRight: 12 }}>—</span>
          Velocità
        </div>
        <div className="ed-hero__main">
          <span className="ed-hero__num">
            {v != null ? <DigitTicker value={v} /> : <em style={{ fontStyle: 'italic', fontWeight: 280, color: 'var(--ed-cream-faint)' }}>—</em>}
          </span>
          <span className="ed-hero__unit">km / h</span>
        </div>
      </div>

      <div className="ed-hero__aside">
        <div className="ed-label" style={{ marginBottom: 6 }}>Ø média</div>
        <span className="ed-hero__aside-num">
          {a != null ? a : '—'}
        </span>
        <span className="ed-hero__aside-unit">km/h moyenne</span>
      </div>
    </section>
  );
}

// Per-digit ticker — simple cross-fade so digits don't shift width.
function DigitTicker({ value }) {
  const str = String(value);
  return (
    <span className="ed-ticker">
      {str.split('').map((d, i) => (
        <DigitCell key={i + ':' + d} digit={d} />
      ))}
    </span>
  );
}
function DigitCell({ digit }) {
  // Use the key change in DigitTicker to retrigger a tiny in-animation.
  const ref = useRefV(null);
  useEV(() => {
    const el = ref.current;
    if (!el) return;
    el.animate(
      [
        { opacity: 0, transform: 'translateY(0.3em)' },
        { opacity: 1, transform: 'translateY(0)' },
      ],
      { duration: 320, easing: 'cubic-bezier(0.32, 0.04, 0.18, 1)' }
    );
  }, []);
  return <span ref={ref} className="ed-ticker__digit"><span>{digit}</span></span>;
}

// ── Telemetry strip ─────────────────────────────────────────────────
function TelemetryStrip({ state, bitrateKbps, readers, visibility, history }) {
  const min = history.length ? Math.round(Math.min(...history)) : null;
  const max = history.length ? Math.round(Math.max(...history)) : null;

  return (
    <section>
      <div className="ed-strip-row">
        <Cell label="Bitrate"  value={state === 'live' && bitrateKbps != null ? `${(bitrateKbps / 1000).toFixed(2)} Mbps` : '—'} />
        <Cell label="Latency"  value={state === 'live' ? '~ 320 ms' : '—'} />
        <Cell label="Audience" value={readers != null ? `${readers}` : '—'} />
        <Cell label="Channel"  value={(visibility || 'public').toUpperCase()} />
      </div>

      {history.length > 0 && (
        <div style={{ paddingTop: 14 }}>
          <div className="ed-label" style={{ marginBottom: 10, color: 'var(--ed-cream-faint)' }}>
            Velocità · last {Math.min(history.length, 60)}s
            <span style={{ marginLeft: 16, fontFamily: 'var(--ed-mono)', textTransform: 'none', letterSpacing: 0 }}>
              min {min ?? '—'}  ·  max {max ?? '—'}
            </span>
          </div>
          <Sparkline data={history} />
        </div>
      )}
    </section>
  );
}

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>
  );
}

// Editorial sparkline — single ferrari hairline trace, no fill.
function Sparkline({ data, width = 880, height = 44 }) {
  if (!data || data.length < 2) return null;
  const max = Math.max(...data, 1);
  const min = Math.min(...data, 0);
  const span = Math.max(1, max - min);
  const pts = data.map((v, i) => {
    const x = (i / (data.length - 1)) * width;
    const y = height - ((v - min) / span) * height;
    return `${x.toFixed(1)},${y.toFixed(1)}`;
  }).join(' ');
  return (
    <svg viewBox={`0 0 ${width} ${height}`} style={{ width: '100%', height, display: 'block' }}>
      <polyline points={pts} fill="none" stroke="var(--ed-ferrari)" strokeWidth="1" />
    </svg>
  );
}

// ── Footer ──────────────────────────────────────────────────────────
function Footer() {
  const yr = new Date().getFullYear();
  return (
    <>
      <hr className="ed-hr" />
      <footer className="ed-footer">
        <div className="ed-footer__left">
          <span style={{ fontStyle: 'italic' }}>RaceCast Editoriale</span>
          <span style={{ color: 'var(--ed-cream-trace)' }}>·</span>
          <span>{yr}</span>
        </div>
        <div className="ed-footer__right">
          <a href="/" style={{ color: 'inherit', textDecoration: 'none', fontStyle: 'italic' }}>← admin</a>
        </div>
      </footer>
    </>
  );
}

// ── helpers ─────────────────────────────────────────────────────────
function formatClock(sec) {
  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}`;
}

window.LiveViewer = LiveViewer;
