/**
 * Turning a design system's RECORDED values into ones you can look at.
 *
 * Replaces `designTokens.ts` and `designDrift.ts`, which between them read the
 * running app's own stylesheet — names out of a bundled `theme.css`, values out
 * of `getComputedStyle(document.documentElement)`. That could only ever describe
 * the install serving the page, and the design surface is for the projects an
 * install TRACKS (#274). What is left here works on any system's record,
 * including one for an app this browser has never loaded.
 *
 * Nothing in this module reads the document's own tokens or mutates the page.
 */

/** The base mode's key in `value_by_mode`, mirroring services/design_stylesheet. */
export const BASE_MODE = "base";

/**
 * Which declared value applies in `mode`.
 *
 * Falls back to base, which is the storage model rather than a convenience: a
 * mode block is an OVERRIDE layer, so a token with no entry for the current
 * mode is not missing — it is inheriting, exactly as the generated sheet has it.
 */
export function valueForMode(
  valueByMode: Record<string, string>,
  mode: string,
): string {
  const own = valueByMode[mode];
  if (own !== undefined && own !== "") return own;
  return valueByMode[BASE_MODE] ?? "";
}

/** Every mode any token in the set declares, base first then the rest by name. */
export function modesPresent(
  tokens: { value_by_mode: Record<string, string> }[],
): string[] {
  const modes = new Set<string>();
  for (const token of tokens) {
    for (const [mode, value] of Object.entries(token.value_by_mode)) {
      if (value) modes.add(mode);
    }
  }
  const rest = [...modes].filter((m) => m !== BASE_MODE).sort();
  return modes.has(BASE_MODE) ? [BASE_MODE, ...rest] : rest;
}

/**
 * Resolve declared values the way a browser would, without applying them.
 *
 * A record holds `color-mix(in srgb, var(--fs-accent) 15%, transparent)`. Shown
 * as text that is a string; shown as a swatch it needs `var()` substituted and
 * the mix evaluated. Rather than write a CSS parser, set the declarations on an
 * offscreen probe and read them back — the substitution is done by the
 * implementation that would do it for real.
 *
 * Custom properties INHERIT, and `all: initial` does not reset them — so a probe
 * sitting in this page would resolve any reference the record leaves undeclared
 * against the surrounding app's own tokens. Previewing another project's system
 * would then quietly borrow this one's palette wherever that system was
 * incomplete, and a token the record already knows is broken (it shows up under
 * `unknown_refs`) would render as though it were fine.
 *
 * So every name referenced but not declared is blanked on the probe first. It
 * resolves to nothing, which is what the record says it is.
 */
const VAR_REFERENCE = /var\(\s*(--[A-Za-z0-9_-]+)/g;

export function resolveDeclared(declared: Map<string, string>): Map<string, string> {
  const probe = document.createElement("div");
  probe.style.display = "none";
  for (const value of declared.values()) {
    for (const match of value.matchAll(VAR_REFERENCE)) {
      if (!declared.has(match[1])) probe.style.setProperty(match[1], " ");
    }
  }
  for (const [name, value] of declared) probe.style.setProperty(name, value);
  document.body.appendChild(probe);
  try {
    const computed = getComputedStyle(probe);
    const out = new Map<string, string>();
    for (const name of declared.keys()) {
      out.set(name, computed.getPropertyValue(name).trim());
    }
    return out;
  } finally {
    probe.remove();
  }
}
