// Shared plumbing for every page: CSRF-injecting fetch, theme toggle,
// sidebar toggle (mobile), and small formatting helpers. No framework,
// no build step -- plain scripts, load order handled by <script> tags.

// Session-cookie auth needs CSRF proof on mutating requests. Wrapping
// fetch once means no call site has to remember the header. The token
// rides a <meta> tag emitted only for session-authed pages.
(function () {
  var meta = document.querySelector('meta[name="csrf-token"]');
  if (!meta || !meta.content) return;
  var CSRF = meta.content;
  var origFetch = window.fetch;
  window.fetch = function (input, init) {
    init = init || {};
    var method = (init.method || (input && input.method) || 'GET').toUpperCase();
    var url = typeof input === 'string' ? input : (input && input.url) || '';
    var sameOrigin = url.indexOf('://') === -1 || url.indexOf(location.origin) === 0;
    if (sameOrigin && method !== 'GET' && method !== 'HEAD') {
      init.headers = new Headers(init.headers || (input && input.headers) || {});
      init.headers.set('X-CSRF-Token', CSRF);
    }
    return origFetch.call(this, input, init);
  };
})();

// Theme toggle: explicit choice wins over the OS preference and is
// remembered; with no explicit choice, CSS falls back to
// prefers-color-scheme on its own. (The pre-paint snippet in the page
// <head> applies the stored theme before first render.)
(function () {
  var btn = document.getElementById('theme-toggle');
  if (!btn) return;

  function currentTheme() {
    var stored = null;
    try { stored = localStorage.getItem('theme'); } catch (e) { /* ignore */ }
    if (stored === 'light' || stored === 'dark') return stored;
    return (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light';
  }

  btn.addEventListener('click', function () {
    var theme = currentTheme() === 'dark' ? 'light' : 'dark';
    document.documentElement.setAttribute('data-theme', theme);
    try { localStorage.setItem('theme', theme); } catch (e) { /* ignore */ }
    document.dispatchEvent(new CustomEvent('themechange', { detail: { theme: theme } }));
  });
})();

// Mobile sidebar: hamburger opens, backdrop or navigation closes.
(function () {
  var shell = document.querySelector('.shell');
  var toggle = document.getElementById('sidebar-toggle');
  var backdrop = document.querySelector('.sidebar-backdrop');
  if (!shell || !toggle) return;
  toggle.addEventListener('click', function () { shell.classList.toggle('sidebar-open'); });
  if (backdrop) {
    backdrop.addEventListener('click', function () { shell.classList.remove('sidebar-open'); });
  }
})();

function showStatus(ok, message) {
  var el = document.getElementById('result');
  if (!el) return;
  el.innerHTML = '<div class="status ' + (ok ? 'ok' : 'err') + '"></div>';
  el.firstChild.textContent = message;
}

// A 409 from a control-gated endpoint means someone else holds the
// frame's control lock -- surface who, plus how to take over.
async function apiError(resp) {
  var text = await resp.text();
  try {
    var body = JSON.parse(text);
    var detail = body.detail !== undefined ? body.detail : body;
    if (detail && detail.error === 'not_controller') {
      var holder = detail.holder || 'Someone else';
      return holder + ' has control of this frame — use "Take control" to make changes.';
    }
    if (typeof detail === 'string') return detail;
  } catch (e) { /* not JSON */ }
  return text;
}

function formatDuration(seconds) {
  var d = Math.floor(seconds / 86400);
  var h = Math.floor((seconds % 86400) / 3600);
  var m = Math.floor((seconds % 3600) / 60);
  if (d > 0) return d + 'd ' + h + 'h';
  if (h > 0) return h + 'h ' + m + 'm';
  return m + 'm';
}

// Reads resolved colors from CSS custom properties rather than
// hardcoding hex values, so canvas drawing matches the current theme.
function themeColor(name) {
  return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
}
