@extends('layouts.overlay')

@section('title', 'SIGN-OFF')

@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: 58vh;
    max-height: 740px;
    min-height: 400px;
  }
</style>
@endsection

@push('data-scripts')
<script>window.__TRACK_COUNT = @json($trackCount);</script>
<script src="{{ asset('data/playlist.js') }}"></script>
@endpush

@section('content')
  @include('partials.hud-strip', [
    'variant' => 'offair',
    'label'   => 'OFF AIR',
    'idText'  => 'OPHI-118 // SIGN-OFF',
    'signalGlyph' => '▮▮▯▯▯',
    'signalProfile' => 'degrading',
  ])

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

  <section class="banner" id="banner">
    <div class="label">— TRANSMISSION ENDED —</div>
    <div class="title" id="bannerTitle">GOOD&nbsp;BYE</div>
    <div class="sub" id="bannerSub">— THANKS FOR TUNING IN —</div>
  </section>

  <footer class="foot">
    <span>CH 118.0 MHz</span>
    <span id="rig">— PROJECT MANIFEST UNLOADED —</span>
    <span class="offair">■ OFF AIR</span>
  </footer>
@endsection

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

  const PROMPT = 'OPHI-118://> ';
  const MODE_KEYS = ['repeat', 'random', 'single'];
  const lines = [
    { kind: 'cmd',  text: 'unloadproject --flush --persist-state' },
    { kind: 'out',  text: 'closing transmission envelope...' },
    { kind: 'gap'                                                 },
    { kind: 'out',  text: 'session       :: archived'             },
    { kind: 'out',  text: 'camera        :: released'             },
    { kind: 'out',  text: 'microphone    :: released'             },
    { kind: 'out',  text: 'capture       :: released'             },
    { kind: 'gap'                                                 },
    { kind: 'dim',  text: 'flushing buffers...'                   },
    { kind: 'gap'                                                 },
    { kind: 'out',  text: 'TRANSMISSION ENDED.'                   },
    { kind: 'warn', text: '— see you on the next channel —'       },
  ];

  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);
    }
    while (term.children.length > 1) {
      let total = 0;
      for (const c of term.children) total += c.getBoundingClientRect().height;
      if (total <= term.clientHeight) break;
      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', '--keep-alive'];
    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 playlistData = window.__PLAYLIST || { tracks: [] };
  const allTracks    = playlistData.tracks || [];
  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 (allTracks.length === 0) {
      append('> ', '[audio] no tracks queued', 'term-dim');
      bootFinished = true;
      return;
    }
    append('> ', `[audio] queue retained: ${allTracks.length} tracks`, 'term-out');
    await sleep(180);
    append('> ', '[audio] subscribing to mpd:state events', '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 {
        const cls = ln.kind === 'dim' ? 'term-dim' : ln.kind === 'warn' ? 'term-warn' : 'term-out';
        append('> ', ln.text, cls);
        await sleep(170);
      }
    }
    await sleep(400);
    startMusic();
  })();

  // "OFFLINE FOR" counter that takes over the banner sub-line after 6s.
  const startMs = Date.now();
  const subEl   = document.getElementById('bannerSub');
  const tickOffline = () => {
    const sec = Math.floor((Date.now() - startMs) / 1000);
    const h = Math.floor(sec / 3600);
    const m = Math.floor((sec % 3600) / 60);
    const s = sec % 60;
    const t = h > 0 ? `${pad(h)}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
    subEl.textContent = `— OFFLINE FOR ${t} —`;
  };
  setTimeout(() => { tickOffline(); setInterval(tickOffline, 500); }, 6000);
</script>
@endsection
