@extends('layouts.overlay')

@section('title', 'PROJECT LOADING')

@section('styles')
<style>
  body.overlay-body {
    display: grid;
    grid-template-rows: auto 1fr auto auto;
    gap: 28px;
  }
  .stage {
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .terminal {
    width: 60vw;
    max-width: 1380px;
    height: 62vh;
    max-height: 820px;
    min-height: 400px;
  }
</style>
@endsection

@push('data-scripts')
<script>
  window.__LOADING = @json($manifest);
  window.__TEL     = @json($telemetry);
  window.__TRACK_COUNT = @json($trackCount);
</script>
@endpush

@section('content')
  @include('partials.hud-strip', [
    'variant' => 'live',
    'idText'  => 'OPHI-118 // PROJECT LOAD',
  ])

  <main class="stage">
    <div class="terminal">
      <div id="termOutput"></div>
    </div>
  </main>

  <section class="countdown" id="cd">
    <div class="label">— STARTING IN —</div>
    <div class="digits" id="cdDigits">--:--</div>
    <div class="sub">— TRANSMISSION INCOMING —</div>
  </section>

  <footer class="foot">
    <span>CH 118.0 MHz</span>
    <span id="rig">— PROJECT MANIFEST LOADED —</span>
    <span class="warn">▲ ARMED</span>
  </footer>
@endsection

@section('scripts')
<script>
  'use strict';
  const pad   = HUD.pad;
  const sleep = ms => new Promise(r => setTimeout(r, ms));

  const data    = window.__LOADING || {};
  const onOff   = b => (b ? 'ENABLED'  : 'DISABLED');
  const upper   = s => (s || '').toUpperCase();
  const cdMin   = data.countdownMin ?? 5;

  const PROMPT  = 'OPHI-118://> ';
  const MODE_KEYS = ['repeat', 'random', 'single'];
  const rigName = (window.__TEL?.rig || 'unknown').toUpperCase();
  const lines = [
    { kind: 'cmd',  text: 'loadproject --manifest' },
    { kind: 'out',  text: `detected rig :: ${rigName} - initializing transmission...` },
    { kind: 'gap'                                    },
    { kind: 'out',  text: `target       :: ${upper(data.game) || '—'}`                          },
    ...(data.subtitle ? [{ kind: 'out', text: `mode         :: ${upper(data.subtitle)}` }] : []),
    { kind: 'out',  text: `camera       :: ${onOff(data.camera     ?? true)}`                  },
    { kind: 'out',  text: `microphone   :: ${onOff(data.microphone ?? true)}`                  },
    { kind: 'out',  text: `countdown    :: ${pad(Math.round(cdMin))}:00`                       },
    { kind: 'gap'                                    },
    { kind: 'dim',  text: 'all systems nominal'      },
    { kind: 'out',  text: 'READY.'                   },
  ];

  const term = document.getElementById('termOutput');
  const MAX_TERM_LINES = 200;
  function pruneTerminal() {
    while (term.children.length > MAX_TERM_LINES) {
      const first = term.firstElementChild;
      if (!first || first === npLine) break;
      term.removeChild(first);
    }
  }
  function append(prompt, text, cls) {
    const div = document.createElement('div');
    div.className = 'term-line';
    if (prompt) {
      const p = document.createElement('span');
      p.className = 'term-prompt';
      p.textContent = prompt;
      div.appendChild(p);
    }
    const t = document.createElement('span');
    if (cls) t.className = cls;
    t.textContent = text;
    div.appendChild(t);
    term.appendChild(div);
    pruneTerminal();
    return t;
  }

  async function typeCommand(text) {
    const t = append(PROMPT, '', 'term-out');
    for (const c of text) {
      t.textContent += c;
      await sleep(38);
    }
    await sleep(280);
  }

  function normalizePlayback(playback) {
    const src = playback && typeof playback === 'object' ? playback : {};
    const normalized = {};
    for (const key of MODE_KEYS) normalized[key] = !!src[key];
    normalized.consume = !!src.consume;
    normalized.volume = Number.isFinite(src.volume) ? src.volume : null;
    return normalized;
  }

  function formatMusicCommand(playback) {
    const p = normalizePlayback(playback);
    const parts = ['music', '--boot'];
    if (p.volume !== null) parts.push(`--volume=${p.volume}%`);
    MODE_KEYS.forEach(key => {
      if (p[key]) parts.push(`--${key}`);
    });
    if (p.consume) parts.push('--consume');
    return parts.join(' ');
  }

  const trackCount = window.__TRACK_COUNT || 0;
  let bootStarted = false;
  let bootFinished = false;
  let lastPlaybackModes = null;
  let pendingState = null;
  let modeLogChain = Promise.resolve();

  let npLine = null;
  function ensureNowPlayingLine() {
    if (npLine) return npLine;
    npLine = document.createElement('div');
    npLine.className = 'term-line term-now-playing';
    npLine.innerHTML =
      '<span class="np-arrow">▶</span>' +
      '<span class="np-title">connecting to daemon…</span>' +
      '<span class="np-time">[--:-- / --:--]</span>';
    term.appendChild(npLine);
    return npLine;
  }
  function logBeforeNp(text, cls = 'term-out') {
    if (!npLine) { append('> ', text, cls); return; }
    const div = document.createElement('div');
    div.className = 'term-line';
    const p = document.createElement('span'); p.className = 'term-prompt'; p.textContent = '> ';
    const t = document.createElement('span'); t.className = cls; t.textContent = text;
    div.appendChild(p); div.appendChild(t);
    term.insertBefore(div, npLine);
    pruneTerminal();
  }
  function fmtClock(s) {
    if (!isFinite(s) || s < 0) return '--:--';
    return `${pad(Math.floor(s / 60))}:${pad(Math.floor(s % 60))}`;
  }

  async function logModeChange(mode, enabled) {
    await typeCommand(`music --${mode}=${enabled ? 'true' : 'false'}`);
    logBeforeNp(`[audio] ${mode} mode ${enabled ? 'enabled' : 'disabled'}`, 'term-out');
  }

  function trackModeChanges(playback) {
    const current = normalizePlayback(playback);
    if (!lastPlaybackModes) {
      lastPlaybackModes = current;
      return;
    }
    MODE_KEYS.forEach(key => {
      if (current[key] !== lastPlaybackModes[key]) {
        modeLogChain = modeLogChain.then(() => logModeChange(key, current[key]));
      }
    });
    lastPlaybackModes = current;
  }

  let lastTrackFile = null;
  function renderMusicState(s) {
    if (!npLine) ensureNowPlayingLine();
    if (lastTrackFile !== null && s.file && s.file !== lastTrackFile) {
      logBeforeNp(`[audio] next: ${s.title || '?'}`);
    }
    if (s.file) lastTrackFile = s.file;
    npLine.querySelector('.np-title').textContent = s.title || '—';
    npLine.querySelector('.np-time').textContent  =
      `[${fmtClock(s.currentTime)} / ${fmtClock(s.duration)}]`;
  }

  async function bootFromState(s) {
    if (bootStarted) return;
    bootStarted = true;
    lastPlaybackModes = normalizePlayback(s.playback);

    await typeCommand(formatMusicCommand(s.playback));
    if (trackCount === 0) {
      append('> ', '[audio] mpd library empty — check music_directory + run `mpc update`', 'term-dim');
      bootFinished = true;
      return;
    }

    append('> ', `[audio] indexed ${trackCount} tracks`, 'term-out');
    await sleep(180);
    append('> ', `[audio] uplink to daemon @ ophi-118://${rigName.toLowerCase()}.audio.bus`, 'term-out');
    await sleep(180);
    append('> ', '[audio] chat ops armed :: !skip · !queue · !info', 'term-out');
    await sleep(180);
    append('> ', '[audio] standby for transmission ✓', 'term-out');
    bootFinished = true;
    if (pendingState) renderMusicState(pendingState);
  }

  function startMusic() {
    append('', '[audio] waiting for mpd state', 'term-dim');
    OBSWSBootstrap.onState((s) => {
      pendingState = s;
      if (!bootStarted) {
        bootFromState(s);
        return;
      }
      if (!bootFinished) return;
      renderMusicState(s);
      trackModeChanges(s.playback);
    }, {
      onClose: () => logBeforeNp('[audio] daemon connection lost — retrying', 'term-dim'),
    });
  }

  (async () => {
    await sleep(450);
    for (const ln of lines) {
      if (ln.kind === 'cmd') {
        await typeCommand(ln.text);
      } else if (ln.kind === 'gap') {
        append('', ' ', '');
        await sleep(80);
      } else {
        append('> ', ln.text, ln.kind === 'dim' ? 'term-dim' : 'term-out');
        await sleep(170);
      }
    }
    await sleep(500);
    startMusic();
  })();

  // Countdown
  const startMs   = Date.now();
  const totalMs   = cdMin * 60 * 1000;
  const cdEl      = document.getElementById('cd');
  const cdDigits  = document.getElementById('cdDigits');
  const cdLabel   = cdEl.querySelector('.label');
  const cdSub     = cdEl.querySelector('.sub');
  let   readyShown = false;
  const tickCountdown = () => {
    const remaining = Math.max(0, totalMs - (Date.now() - startMs));
    if (remaining <= 0) {
      if (!readyShown) {
        readyShown = true;
        cdEl.classList.add('ready');
        cdLabel.textContent  = '— STREAM ACTIVE —';
        cdDigits.textContent = 'READY';
        cdSub.textContent    = '— TRANSMISSION GO —';
      }
      return;
    }
    const m = Math.floor(remaining / 60000);
    const s = Math.floor((remaining % 60000) / 1000);
    cdDigits.textContent = `${pad(m)}:${pad(s)}`;
  };
  tickCountdown();
  setInterval(tickCountdown, 250);
</script>
@endsection
