// Homepage — Australis Skyfall theme switch.
//
// WHY THIS EXISTS INSTEAD OF HOMEPAGE'S OWN TOGGLE
// Homepage renders a built-in light/dark switch only when settings.yaml does
// NOT pin `theme:`. We cannot unpin it: removing the key makes the page's data
// loader throw, and its catch branch serves `initialSettings: {}` — a dashboard
// with no tab bar, no layout and no i18n. Measured on 2026-08-24: with
// `theme: dark` present the payload is correct within ~12s of a recreate;
// with the key removed, six recreates over seven minutes all came up empty,
// and putting the key back fixed it on the next try. So the key stays, and the
// switch is ours.
//
// HOW IT WORKS
// Skyfall keys its light theme off `[data-theme="light"]` on <html>. Homepage
// keeps its own `dark` class there regardless — that is fine and was verified:
// with `class="dark scheme-dark theme-slate"` AND `data-theme="light"`, every
// themed surface resolves to Skyfall Day, because our rules carry !important
// on the surfaces Tailwind's `dark:` variants would otherwise claim.
//
// Precedence, highest first:
//   1. an explicit choice stored here in localStorage
//   2. the OS preference, via @media (prefers-color-scheme) in custom.css
//   3. dark — Skyfall's first-class default
//
// The stylesheet handles 2 and 3 on its own, so this file is only responsible
// for 1. It writes `data-theme` ONLY when there is a stored choice; leaving the
// attribute absent is what lets the media query take over.

(() => {
  "use strict";

  const KEY = "skyfall-theme"; // "light" | "dark" | absent = follow the OS
  const root = document.documentElement;

  const stored = () => {
    try {
      const v = localStorage.getItem(KEY);
      return v === "light" || v === "dark" ? v : null;
    } catch {
      return null; // private mode / storage disabled — fall through to the OS
    }
  };

  const systemPrefersLight = () =>
    window.matchMedia?.("(prefers-color-scheme: light)").matches ?? false;

  const apply = (mode) => {
    if (mode) root.setAttribute("data-theme", mode);
    else root.removeAttribute("data-theme");
  };

  // Run before first paint where possible, so a stored light choice does not
  // flash dark on load.
  apply(stored());

  const effective = () => stored() ?? (systemPrefersLight() ? "light" : "dark");

  const ICON = {
    // Lucide sun / moon, 1.75 stroke per Skyfall's iconography note.
    light:
      '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>',
    dark: '<path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/>',
  };

  const button = document.createElement("button");
  button.type = "button";
  button.id = "skyfall-theme-toggle";
  button.setAttribute("aria-label", "Toggle light and dark theme");

  const paint = () => {
    const mode = effective();
    // Show the icon for the mode you would switch TO, which is the convention
    // every OS theme switch uses.
    const next = mode === "light" ? "dark" : "light";
    button.title = `Switch to ${next} theme`;
    button.setAttribute("aria-pressed", String(mode === "light"));
    button.innerHTML =
      '<svg viewBox="0 0 24 24" width="16" height="16" fill="none" ' +
      'stroke="currentColor" stroke-width="1.75" stroke-linecap="round" ' +
      `stroke-linejoin="round">${ICON[next]}</svg>`;
  };

  button.addEventListener("click", () => {
    const next = effective() === "light" ? "dark" : "light";
    try {
      localStorage.setItem(KEY, next);
    } catch {
      /* storage unavailable — the choice just will not survive a reload */
    }
    apply(next);
    paint();
  });

  // Track the OS while no explicit choice is stored, so the icon stays honest.
  window
    .matchMedia?.("(prefers-color-scheme: light)")
    .addEventListener?.("change", () => {
      if (!stored()) paint();
    });

  // Homepage renders client-side and rebuilds its header, so a one-shot
  // querySelector at load usually finds nothing and would silently no-op.
  // Watch until the header strip exists, then stop watching.
  const mount = () => {
    if (document.getElementById("skyfall-theme-toggle")?.isConnected) return true;
    const host = document.querySelector(
      "div.flex.flex-row.self-center.flex-wrap.justify-between",
    );
    if (!host) return false;
    paint();
    host.appendChild(button);
    return true;
  };

  if (!mount()) {
    const observer = new MutationObserver(() => {
      if (mount()) observer.disconnect();
    });
    observer.observe(document.body, { childList: true, subtree: true });
    // Backstop: never leave an observer running forever on a page that will
    // not produce the host element.
    setTimeout(() => observer.disconnect(), 30000);
  }
})();
