export type ThemePref = 'system' | 'light' | 'dark';
export type ResolvedTheme = 'light' | 'dark';

export const THEME_STORAGE_KEY = 'maestro.theme';

export function isThemePref(v: unknown): v is ThemePref {
  return v === 'system' || v === 'light' || v === 'dark';
}

/** Resolve the effective theme from the stored preference + OS state. Pure. */
export function resolveTheme(pref: ThemePref, systemPrefersDark: boolean): ResolvedTheme {
  if (pref === 'dark') return 'dark';
  if (pref === 'light') return 'light';
  return systemPrefersDark ? 'dark' : 'light';
}

export function readStoredTheme(): ThemePref {
  try {
    const v = localStorage.getItem(THEME_STORAGE_KEY);
    return isThemePref(v) ? v : 'system';
  } catch {
    return 'system';
  }
}

export function writeStoredTheme(pref: ThemePref): void {
  try {
    localStorage.setItem(THEME_STORAGE_KEY, pref);
  } catch {
    /* private mode / disabled storage — ignore */
  }
}

/** Apply the resolved theme to the document root (drives [data-theme="…"]). */
export function applyTheme(root: HTMLElement, resolved: ResolvedTheme): void {
  root.dataset.theme = resolved;
}

const DARK_MQ = '(prefers-color-scheme: dark)';

/** True if the OS currently prefers a dark color scheme. */
export function systemPrefersDark(): boolean {
  return typeof window !== 'undefined' && window.matchMedia(DARK_MQ).matches;
}

export const THEME_CHANGE_EVENT = 'maestro:theme-pref-changed';

/**
 * Persist a new preference and apply it to the document immediately. Used by
 * the theme toggle UI. The pure resolution lives in resolveTheme.
 */
export function setThemePref(pref: ThemePref): void {
  writeStoredTheme(pref);
  applyTheme(document.documentElement, resolveTheme(pref, systemPrefersDark()));
  try {
    // Carry the chosen pref in the event so listeners reflect it even if the
    // localStorage write above failed (private mode) — re-reading storage
    // would otherwise fall back to 'system' and desync the toggle.
    window.dispatchEvent(new CustomEvent(THEME_CHANGE_EVENT, { detail: pref }));
  } catch {
    /* non-browser context */
  }
}

/**
 * Wire runtime theme. The INITIAL attribute is set by the inline script in
 * index.html (pre-paint, no FOUC); this keeps it live and re-applies when the
 * OS theme changes while the stored pref is 'system'. Returns an unsubscribe.
 */
export function initTheme(): () => void {
  const mq = window.matchMedia(DARK_MQ);
  const sync = () =>
    applyTheme(document.documentElement, resolveTheme(readStoredTheme(), mq.matches));
  sync();
  mq.addEventListener('change', sync);
  return () => mq.removeEventListener('change', sync);
}
