// Apply the server-resolved theme to the document as CSS custom properties.
//
// The effective token set is resolved server-side and arrives on
// `settings.theme` (see server/src/utils/themeResolve.js). The client's only
// job is to write it onto <html> — and, crucially, to take back what it wrote
// last time, which is the part with actual logic and the reason this lives in
// its own testable module.
//
// Why removal matters: an admin who resets the theme, or switches from a preset
// that sets --bg to one that does not, gets a payload that no longer mentions
// that variable. Inline properties are not cleared by writing a smaller object
// over them, so without an explicit removeProperty the old value would stick
// until a reload. That would make "Reset to defaults" look broken.
//
// Everything written here is a value the server validated against a closed set
// (hex color, curated font stack, bounded px length, listed shadow). The client
// deliberately does not re-validate — it would be a second, drifting authority.
// It does refuse anything that is not a `--custom-property`, which is the one
// check that costs nothing and stops a token map from reaching an ordinary CSS
// property.

const CUSTOM_PROPERTY = /^--[a-zA-Z0-9-_]+$/

/**
 * @param {CSSStyleDeclaration} style  usually document.documentElement.style
 * @param {Record<string, string>|null|undefined} tokens  the new theme, or
 *   null/absent for "no admin theme" — which clears everything previously set
 * @param {string[]} [applied]  the keys this function wrote last time
 * @returns {string[]} the keys now applied, to pass back on the next call
 */
export function applyThemeTokens(style, tokens, applied = []) {
  const next = []
  if (tokens && typeof tokens === 'object') {
    for (const [name, value] of Object.entries(tokens)) {
      if (!CUSTOM_PROPERTY.test(name) || typeof value !== 'string' || value === '') continue
      style.setProperty(name, value)
      next.push(name)
    }
  }
  // Take back only what we set ourselves. Anything else on the element's inline
  // style belongs to someone else (SiteContext's own --accent line, a future
  // feature) and is not ours to clear.
  for (const name of applied) {
    if (!next.includes(name)) style.removeProperty(name)
  }
  return next
}
