/* The page an operator lands on. It answers one question — is anything wrong — and hands
   off to the page that can do something about it. Nothing here is editable on purpose:
   somewhere that both summarises and changes state is where an accidental click lives. */

const { fmt, ui, $ } = Admin;

function row(label, value) {
  return '<div class="kv-row"><span>' + fmt.escape(label) + '</span><span>' + value + '</span></div>';
}

Admin.onStatus((status) => {
  const features = status.features || {};
  const featureList = features.features || [];
  const clients = status.clients || [];
  const online = clients.filter((client) => fmt.recent(client.lastSeen)).length;

  // The marks are the areas these numbers belong to, in the tones the rest of the console
  // uses for them: the library is teal wherever it is counted, a person is violet, a
  // television is blue. They are the same on the pages these tiles link through to.
  $('overview-tiles').innerHTML = ui.tiles([
    ['items in the library', fmt.number(status.library.total), { icon: 'library', tone: 'data' }],
    ['people signed in', fmt.number((status.requestUsers || []).length),
      { icon: 'people', tone: 'note' }],
    ['devices · ' + online + ' active now', fmt.number(clients.length),
      { icon: 'tv', tone: 'info' }],
    ['optional features on', featureList.filter((feature) => feature.enabled).length +
      ' / ' + featureList.length, { icon: 'sliders', tone: 'ok' }],
    ['last import', fmt.when(status.library.lastSynced), { small: true, icon: 'clock' }],
  ]);

  const maintenance = status.maintenance || {};
  const policy = status.updatePolicy || {};
  const required = Boolean(policy.minimumVersion) && policy.minimumVersion === policy.latestVersion;
  const playback = status.playbackPolicy || {};
  $('overview-state').innerHTML =
    row('Availability', maintenance.enabled
      ? ui.tag('offline for maintenance', 'bad')
      : ui.tag('online', 'ok')) +
    row('Feature control plane', features.safeMode
      ? ui.tag('safe mode · optional features off', 'warn')
      : ui.tag('revision r' + fmt.number(features.revision || 0), 'ok')) +
    row('App update prompt', !policy.enabled
      ? ui.tag('off', 'idle')
      : ui.tag((required ? 'required · ' : 'optional · ') + policy.latestVersion,
        required ? 'warn' : 'ok')) +
    row('Catalogue import', status.syncRunning
      ? ui.tag('running', 'warn')
      : ui.tag('every ' + status.syncEvery, 'idle')) +
    row('Playback preroll', playback.prerollEnabled === false
      ? ui.tag('off', 'idle')
      : ui.tag(((playback.prerollDurationMs || 6500) / 1000) + 's', 'ok'));

  const mdblist = status.mdblist || {};
  const forYou = status.forYou || {};
  $('overview-services').innerHTML =
    row('Radarr', status.radarrReady ? ui.tag('ready', 'ok') : ui.tag('not configured', 'idle')) +
    row('Sonarr', status.sonarrReady ? ui.tag('ready', 'ok') : ui.tag('not configured', 'idle')) +
    row('MDBList ratings', mdblist.enabled
      ? ui.tag(fmt.number(mdblist.cachedTitles) + ' titles stored', 'ok')
      : ui.tag(mdblist.apiKeyConfigured ? 'off · key saved' : 'off · no key', 'idle')) +
    row('For You pools', status.forYouRunning
      ? ui.tag('rebuilding', 'warn')
      : ui.tag(fmt.number(forYou.candidates) + ' ranked candidates', 'idle')) +
    row('Recommendation profiles', '<span class="code">' + fmt.number(forYou.profiles) + '</span>');

  const runs = (status.runs || []).slice(0, 5);
  $('overview-runs').innerHTML = runs.length ? runs.map((run) => {
    const tone = run.status === 'success' ? 'ok' : run.status === 'running' ? 'warn' : 'bad';
    return '<tr><td>' + fmt.escape(fmt.when(run.startedAt)) + '</td>' +
      '<td>' + fmt.escape(run.kind) + '</td>' +
      '<td>' + ui.tag(run.status, tone) + '</td>' +
      '<td class="num">' + fmt.number(run.itemsUpserted) + '</td></tr>';
  }).join('') : ui.emptyRow(4, 'No imports have run yet.');
});

// Process statistics are their own endpoint and their own tick: they are the one thing here
// that says nothing about the household and everything about the container.
Admin.onRefresh(async () => {
  const runtime = await Admin.api('/admin/api/runtime');
  $('overview-runtime').innerHTML = ui.tiles([
    ['goroutines', fmt.number(runtime.goroutines), { icon: 'pulse', tone: 'info' }],
    ['heap in use', fmt.bytes(runtime.heapInuse), { icon: 'chip', tone: 'info' }],
    ['reserved from the OS', fmt.bytes(runtime.sys), { icon: 'chip', tone: 'info' }],
    ['collections', fmt.number(runtime.numGc), { icon: 'sync', tone: 'info' }],
  ]);
  const limit = runtime.memoryLimit > 0 && runtime.memoryLimit < Number.MAX_SAFE_INTEGER
    ? fmt.bytes(runtime.memoryLimit) + (runtime.configuredLimit ? ' (GOMEMLIMIT)' : '')
    : 'no limit set';
  $('overview-memory').textContent = 'Next collection at ' + fmt.bytes(runtime.nextGc) +
    ' · memory limit ' + limit + ' · ' + runtime.gomaxprocs + ' processors available.';
});
