const crypto = require("crypto");
const { db } = require("./db");
const { getSetting, setSetting } = require("./settings");

const DEFAULT_THEME_ID = "builtin:lumi-default";
const THEME_SYSTEM_VERSION = 1;
const COLOR_PATTERN = /^#[0-9a-f]{6}$/i;
const FONT_STACKS = Object.freeze({
  lumi: {
    label: "Lumi Sans",
    stack: '"Source Sans 3", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
  },
  system: {
    label: "System UI",
    stack: 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
  },
  rounded: {
    label: "Rounded",
    stack: 'ui-rounded, "Nunito Sans", "Aptos", Inter, ui-sans-serif, system-ui, sans-serif'
  },
  editorial: {
    label: "Editorial",
    stack: 'Georgia, "Times New Roman", ui-serif, serif'
  },
  mono: {
    label: "Mono",
    stack: '"Cascadia Code", "SFMono-Regular", Consolas, "Liberation Mono", monospace'
  }
});

const TYPOGRAPHY_FIELDS = ["bodyFont", "displayFont", "monoFont", "baseSize", "headingScale", "controlScale"];

const MODE_COLOR_FIELDS = [
  "bg1",
  "bg2",
  "bg3",
  "text",
  "muted",
  "accent",
  "accentAlt",
  "success",
  "warning",
  "danger",
  "info",
  "surface",
  "surface2",
  "surface3",
  "border",
  "link",
  "buttonBg",
  "buttonText",
  "buttonHover",
  "inputBg",
  "inputBorder",
  "inputText",
  "focusRing"
];

const ROLE_COLOR_FIELDS = ["public", "mod", "admin"];

const DEFAULT_MODE_LIGHT = {
  bg1: "#dff4f2",
  bg2: "#f5f7f8",
  bg3: "#fff1dc",
  text: "#182026",
  muted: "#5a6872",
  accent: "#176b75",
  accentAlt: "#e58b2b",
  success: "#23845b",
  warning: "#a96612",
  danger: "#bd4d4d",
  info: "#3479a8",
  surface: "#ffffff",
  surface2: "#f4f7f8",
  surface3: "#edf2f3",
  border: "#d8e0e3",
  link: "#0d6470",
  buttonBg: "#176b75",
  buttonText: "#ffffff",
  buttonHover: "#0f5660",
  inputBg: "#ffffff",
  inputBorder: "#c8d3d7",
  inputText: "#182026",
  focusRing: "#2f98a5"
};

const DEFAULT_MODE_DARK = {
  bg1: "#102c31",
  bg2: "#11171b",
  bg3: "#261e18",
  text: "#f2f6f7",
  muted: "#aebbc1",
  accent: "#63c4cf",
  accentAlt: "#f0b45f",
  success: "#59c894",
  warning: "#e4b35d",
  danger: "#ef7b78",
  info: "#74b9e6",
  surface: "#1a2227",
  surface2: "#202b31",
  surface3: "#27343b",
  border: "#35434a",
  link: "#7bd3dc",
  buttonBg: "#4dafba",
  buttonText: "#08191c",
  buttonHover: "#68c8d2",
  inputBg: "#151d21",
  inputBorder: "#42535b",
  inputText: "#f2f6f7",
  focusRing: "#7bd3dc"
};

const DEFAULT_THEME_VALUES = {
  light: DEFAULT_MODE_LIGHT,
  dark: DEFAULT_MODE_DARK,
  role: {
    public: "#ffffff",
    mod: "#23845b",
    admin: "#bd4d6d"
  },
  metrics: {
    radius: 14,
    shadowStrength: 0.14,
    spacingScale: 1
  },
  typography: {
    bodyFont: "lumi",
    displayFont: "system",
    monoFont: "mono",
    baseSize: 16,
    headingScale: 1,
    controlScale: 1,
    bodyFontStack: FONT_STACKS.lumi.stack,
    displayFontStack: FONT_STACKS.system.stack,
    monoFontStack: FONT_STACKS.mono.stack
  }
};

function mergeMode(base, override = {}) {
  return Object.fromEntries(
    MODE_COLOR_FIELDS.map((field) => [field, override[field] || base[field]])
  );
}

function createBuiltin(id, name, description, overrides = {}) {
  return Object.freeze({
    id: `builtin:${id}`,
    name,
    description,
    builtin: true,
    readOnly: true,
    baseThemeId: null,
    light: mergeMode(DEFAULT_MODE_LIGHT, overrides.light),
    dark: mergeMode(DEFAULT_MODE_DARK, overrides.dark),
    role: { ...DEFAULT_THEME_VALUES.role, ...(overrides.role || {}) },
    metrics: { ...DEFAULT_THEME_VALUES.metrics, ...(overrides.metrics || {}) },
    typography: normalizeTypography(overrides.typography, DEFAULT_THEME_VALUES.typography)
  });
}

const BUILTIN_THEMES = [
  createBuiltin(
    "lumi-default",
    "Lumi Default",
    "Balanced teal and warm accents with automatic light and dark modes."
  ),
  createBuiltin("lumi-dark", "Lumi Dark", "A deep, low-glare theme for dark workspaces.", {
    light: {
      bg1: "#18242a",
      bg2: "#11171b",
      bg3: "#241d19",
      text: "#f3f6f7",
      muted: "#b4c0c5",
      surface: "#1c252a",
      surface2: "#222d33",
      surface3: "#29363d",
      border: "#3b4a52",
      inputBg: "#141c20",
      inputBorder: "#465860",
      inputText: "#f3f6f7",
      accent: "#67c6d0",
      link: "#7bd3dc",
      buttonBg: "#51b4bf",
      buttonText: "#08191c",
      buttonHover: "#71d0da",
      focusRing: "#7bd3dc"
    }
  }),
  createBuiltin("lumi-light", "Lumi Light", "A crisp, bright theme with restrained shadows.", {
    dark: DEFAULT_MODE_LIGHT,
    metrics: { shadowStrength: 0.08 }
  }),
  createBuiltin("high-contrast", "High Contrast", "Maximum clarity with strong focus and status colors.", {
    light: {
      bg1: "#ffffff",
      bg2: "#ffffff",
      bg3: "#f2f2f2",
      text: "#000000",
      muted: "#303030",
      accent: "#004f5a",
      accentAlt: "#8a4300",
      success: "#006b3c",
      warning: "#7a4700",
      danger: "#a00000",
      info: "#004b88",
      surface: "#ffffff",
      surface2: "#f5f5f5",
      surface3: "#e8e8e8",
      border: "#555555",
      link: "#003f99",
      buttonBg: "#003f49",
      buttonText: "#ffffff",
      buttonHover: "#002c33",
      inputBg: "#ffffff",
      inputBorder: "#333333",
      inputText: "#000000",
      focusRing: "#005fcc"
    },
    dark: {
      bg1: "#000000",
      bg2: "#000000",
      bg3: "#101010",
      text: "#ffffff",
      muted: "#d6d6d6",
      accent: "#67e8f9",
      accentAlt: "#ffd166",
      success: "#65e6a3",
      warning: "#ffd166",
      danger: "#ff8c8c",
      info: "#8fd3ff",
      surface: "#080808",
      surface2: "#151515",
      surface3: "#222222",
      border: "#aaaaaa",
      link: "#8fd3ff",
      buttonBg: "#a5f3fc",
      buttonText: "#000000",
      buttonHover: "#ffffff",
      inputBg: "#000000",
      inputBorder: "#dddddd",
      inputText: "#ffffff",
      focusRing: "#ffffff"
    },
    metrics: { radius: 8, shadowStrength: 0, spacingScale: 1.05 }
  }),
  createBuiltin("midnight", "Midnight", "Cool blue surfaces with violet highlights.", {
    light: {
      bg1: "#dce8ff",
      bg2: "#f4f6fb",
      bg3: "#eee8ff",
      accent: "#4457a6",
      accentAlt: "#8258b7",
      link: "#354a9b",
      buttonBg: "#4457a6",
      buttonHover: "#34448a",
      focusRing: "#6f82d8"
    },
    dark: {
      bg1: "#10182f",
      bg2: "#0b1020",
      bg3: "#211630",
      surface: "#141c32",
      surface2: "#19233d",
      surface3: "#202c49",
      border: "#334160",
      accent: "#91a4ff",
      accentAlt: "#c49aff",
      link: "#aab8ff",
      buttonBg: "#91a4ff",
      buttonText: "#0b1020",
      buttonHover: "#b0bcff",
      focusRing: "#c49aff"
    }
  }),
  createBuiltin("soft-aurora", "Soft Aurora", "A gentle mint, lavender, and coral palette.", {
    light: {
      bg1: "#dcf8ee",
      bg2: "#f8f5fb",
      bg3: "#ffe9e4",
      accent: "#397f70",
      accentAlt: "#986aa8",
      link: "#306f63",
      buttonBg: "#397f70",
      buttonHover: "#2d665a",
      focusRing: "#8a6fa8"
    },
    dark: {
      bg1: "#17352f",
      bg2: "#171a22",
      bg3: "#38242e",
      surface: "#20262d",
      surface2: "#283038",
      surface3: "#313b44",
      border: "#43505a",
      accent: "#82d7c1",
      accentAlt: "#d4a7e1",
      link: "#9ce7d4",
      buttonBg: "#72c9b3",
      buttonText: "#10231f",
      buttonHover: "#96e3d0",
      focusRing: "#d4a7e1"
    },
    metrics: { radius: 18, shadowStrength: 0.1, spacingScale: 1.05 }
  })
];

const BUILTIN_MAP = new Map(BUILTIN_THEMES.map((theme) => [theme.id, theme]));

function cloneTheme(theme) {
  return JSON.parse(JSON.stringify(theme));
}

function getBuiltinTheme(id = DEFAULT_THEME_ID) {
  return BUILTIN_MAP.get(id) || BUILTIN_MAP.get(DEFAULT_THEME_ID);
}

function customKey(id) {
  return `custom:${id}`;
}

function customId(themeId) {
  return String(themeId || "").startsWith("custom:")
    ? String(themeId).slice("custom:".length)
    : null;
}

function normalizeThemeValues(values, baseTheme = getBuiltinTheme()) {
  const source = values && typeof values === "object" ? values : {};
  const normalized = {
    light: mergeMode(baseTheme.light, source.light),
    dark: mergeMode(baseTheme.dark, source.dark),
    role: { ...baseTheme.role, ...(source.role || {}) },
    metrics: { ...baseTheme.metrics, ...(source.metrics || {}) },
    typography: normalizeTypography(source.typography, baseTheme.typography)
  };

  for (const mode of ["light", "dark"]) {
    for (const field of MODE_COLOR_FIELDS) {
      if (!COLOR_PATTERN.test(normalized[mode][field])) {
        normalized[mode][field] = baseTheme[mode][field];
      }
    }
  }
  for (const field of ROLE_COLOR_FIELDS) {
    if (!COLOR_PATTERN.test(normalized.role[field])) {
      normalized.role[field] = baseTheme.role[field];
    }
  }
  normalized.metrics.radius = clampNumber(
    normalized.metrics.radius,
    0,
    32,
    baseTheme.metrics.radius
  );
  normalized.metrics.shadowStrength = clampNumber(
    normalized.metrics.shadowStrength,
    0,
    0.35,
    baseTheme.metrics.shadowStrength
  );
  normalized.metrics.spacingScale = clampNumber(
    normalized.metrics.spacingScale,
    0.75,
    1.35,
    baseTheme.metrics.spacingScale
  );
  for (const mode of ["light", "dark"]) {
    if (contrastRatio(normalized[mode].text, normalized[mode].surface) < 4.5) {
      normalized[mode].text = baseTheme[mode].text;
      normalized[mode].surface = baseTheme[mode].surface;
    }
    if (contrastRatio(normalized[mode].buttonText, normalized[mode].buttonBg) < 4.5) {
      normalized[mode].buttonText = baseTheme[mode].buttonText;
      normalized[mode].buttonBg = baseTheme[mode].buttonBg;
    }
    if (contrastRatio(normalized[mode].inputText, normalized[mode].inputBg) < 4.5) {
      normalized[mode].inputText = baseTheme[mode].inputText;
      normalized[mode].inputBg = baseTheme[mode].inputBg;
    }
  }
  return normalized;
}

function normalizeFontKey(value, fallback) {
  return Object.prototype.hasOwnProperty.call(FONT_STACKS, value) ? value : fallback;
}

function normalizeTypography(values = {}, fallback = DEFAULT_THEME_VALUES.typography) {
  const bodyFont = normalizeFontKey(values.bodyFont, fallback.bodyFont);
  const displayFont = normalizeFontKey(values.displayFont, fallback.displayFont);
  const monoFont = normalizeFontKey(values.monoFont, fallback.monoFont);
  return {
    bodyFont,
    displayFont,
    monoFont,
    baseSize: clampNumber(values.baseSize, 14, 19, fallback.baseSize),
    headingScale: clampNumber(values.headingScale, 0.9, 1.2, fallback.headingScale),
    controlScale: clampNumber(values.controlScale, 0.9, 1.12, fallback.controlScale),
    bodyFontStack: FONT_STACKS[bodyFont].stack,
    displayFontStack: FONT_STACKS[displayFont].stack,
    monoFontStack: FONT_STACKS[monoFont].stack
  };
}

function clampNumber(value, min, max, fallback) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed)) return fallback;
  return Math.min(max, Math.max(min, parsed));
}

function parseHex(value) {
  const raw = String(value || "").slice(1);
  return [0, 2, 4].map((index) => Number.parseInt(raw.slice(index, index + 2), 16));
}

function relativeLuminance(value) {
  const channels = parseHex(value).map((channel) => {
    const normalized = channel / 255;
    return normalized <= 0.03928
      ? normalized / 12.92
      : Math.pow((normalized + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
}

function contrastRatio(left, right) {
  const a = relativeLuminance(left);
  const b = relativeLuminance(right);
  return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
}

function validateThemeValues(values) {
  const errors = [];
  for (const mode of ["light", "dark"]) {
    for (const field of MODE_COLOR_FIELDS) {
      if (!COLOR_PATTERN.test(String(values?.[mode]?.[field] || ""))) {
        errors.push(`${mode}.${field} must be a six-digit hex color.`);
      }
    }
  }
  for (const field of ROLE_COLOR_FIELDS) {
    if (!COLOR_PATTERN.test(String(values?.role?.[field] || ""))) {
      errors.push(`role.${field} must be a six-digit hex color.`);
    }
  }

  const metricRules = [
    ["radius", 0, 32],
    ["shadowStrength", 0, 0.35],
    ["spacingScale", 0.75, 1.35]
  ];
  for (const [field, min, max] of metricRules) {
    const value = Number(values?.metrics?.[field]);
    if (!Number.isFinite(value) || value < min || value > max) {
      errors.push(`metrics.${field} must be between ${min} and ${max}.`);
    }
  }

  for (const field of ["bodyFont", "displayFont", "monoFont"]) {
    if (!Object.prototype.hasOwnProperty.call(FONT_STACKS, values?.typography?.[field])) {
      errors.push(`typography.${field} must be a supported font preset.`);
    }
  }

  const typographyRules = [
    ["baseSize", 14, 19],
    ["headingScale", 0.9, 1.2],
    ["controlScale", 0.9, 1.12]
  ];
  for (const [field, min, max] of typographyRules) {
    const value = Number(values?.typography?.[field]);
    if (!Number.isFinite(value) || value < min || value > max) {
      errors.push(`typography.${field} must be between ${min} and ${max}.`);
    }
  }

  if (!errors.length) {
    for (const mode of ["light", "dark"]) {
      if (contrastRatio(values[mode].text, values[mode].surface) < 4.5) {
        errors.push(`${mode} text and surface colors need at least 4.5:1 contrast.`);
      }
      if (contrastRatio(values[mode].buttonText, values[mode].buttonBg) < 4.5) {
        errors.push(`${mode} button text and background need at least 4.5:1 contrast.`);
      }
      if (contrastRatio(values[mode].inputText, values[mode].inputBg) < 4.5) {
        errors.push(`${mode} input text and background need at least 4.5:1 contrast.`);
      }
    }
  }
  return errors;
}

function legacyThemeValues() {
  const legacy = {
    light: {
      bg1: getSetting("theme_light_bg_1", "#ffe5c4"),
      bg2: getSetting("theme_light_bg_2", "#f4efe8"),
      bg3: getSetting("theme_light_bg_3", "#e9f3f1"),
      text: getSetting("theme_light_text", "#121518"),
      muted: getSetting("theme_light_text_muted", "#2c3137"),
      accent: getSetting("theme_light_accent", "#0f6a78"),
      accentAlt: getSetting("theme_light_accent_alt", "#f4a340"),
      danger: getSetting("theme_light_danger", "#d66d5c"),
      surface: getSetting("theme_light_surface", "#ffffff"),
      surface2: getSetting("theme_light_surface_2", "#fbf9f6"),
      surface3: getSetting("theme_light_surface_3", "#f9f5ef"),
      border: getSetting("theme_light_border", "#e3ddd6")
    },
    dark: {
      bg1: getSetting("theme_dark_bg_1", "#1b1d1f"),
      bg2: getSetting("theme_dark_bg_2", "#16181b"),
      bg3: getSetting("theme_dark_bg_3", "#0f1113"),
      text: getSetting("theme_dark_text", "#f2f0ec"),
      muted: getSetting("theme_dark_text_muted", "#c5bfb7"),
      accent: getSetting("theme_dark_accent", "#4fb6c2"),
      accentAlt: getSetting("theme_dark_accent_alt", "#f1b765"),
      danger: getSetting("theme_dark_danger", "#e08173"),
      surface: getSetting("theme_dark_surface", "#232629"),
      surface2: getSetting("theme_dark_surface_2", "#2b2f33"),
      surface3: getSetting("theme_dark_surface_3", "#30353a"),
      border: getSetting("theme_dark_border", "#34393d")
    },
    role: {
      public: getSetting("theme_role_public", "#ffffff"),
      mod: getSetting("theme_role_mod", "#2cb678"),
      admin: getSetting("theme_role_admin", "#e35678")
    }
  };
  return normalizeThemeValues(legacy, getBuiltinTheme());
}

function legacyWasCustomized(values) {
  const defaults = {
    light: {
      bg1: "#ffe5c4", bg2: "#f4efe8", bg3: "#e9f3f1", text: "#121518",
      muted: "#2c3137", accent: "#0f6a78", accentAlt: "#f4a340",
      danger: "#d66d5c", surface: "#ffffff", surface2: "#fbf9f6",
      surface3: "#f9f5ef", border: "#e3ddd6"
    },
    dark: {
      bg1: "#1b1d1f", bg2: "#16181b", bg3: "#0f1113", text: "#f2f0ec",
      muted: "#c5bfb7", accent: "#4fb6c2", accentAlt: "#f1b765",
      danger: "#e08173", surface: "#232629", surface2: "#2b2f33",
      surface3: "#30353a", border: "#34393d"
    },
    role: { public: "#ffffff", mod: "#2cb678", admin: "#e35678" }
  };
  return ["light", "dark", "role"].some((group) =>
    Object.entries(defaults[group]).some(([key, value]) => values[group][key] !== value)
  );
}

function ensureThemeMigration() {
  if (Number(getSetting("theme_system_version", 0)) >= THEME_SYSTEM_VERSION) return;
  const legacy = legacyThemeValues();
  setSetting("theme_system_version", THEME_SYSTEM_VERSION);
  if (legacyWasCustomized(legacy)) {
    const theme = insertCustomTheme("Migrated Theme", DEFAULT_THEME_ID, legacy);
    setSetting("theme_active_id", theme.id);
  } else {
    setSetting("theme_active_id", DEFAULT_THEME_ID);
  }
}

function rowToTheme(row) {
  const base = getBuiltinTheme(row.base_theme_id);
  let stored = {};
  try {
    stored = JSON.parse(row.values_json);
  } catch {
    stored = {};
  }
  const values = normalizeThemeValues(stored, base);
  return {
    id: customKey(row.id),
    name: row.name,
    description: `Custom theme based on ${base.name}.`,
    builtin: false,
    readOnly: false,
    baseThemeId: base.id,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
    ...values
  };
}

function getThemeById(themeId) {
  ensureThemeMigration();
  if (BUILTIN_MAP.has(themeId)) return cloneTheme(BUILTIN_MAP.get(themeId));
  const id = customId(themeId);
  if (!id) return null;
  const row = db.prepare("SELECT * FROM custom_themes WHERE id = ?").get(id);
  return row ? rowToTheme(row) : null;
}

function listThemes() {
  ensureThemeMigration();
  const custom = db
    .prepare("SELECT * FROM custom_themes ORDER BY lower(name), created_at")
    .all()
    .map(rowToTheme);
  return [...BUILTIN_THEMES.map(cloneTheme), ...custom];
}

function getActiveTheme() {
  ensureThemeMigration();
  const requested = getSetting("theme_active_id", DEFAULT_THEME_ID);
  const theme = getThemeById(requested) || cloneTheme(getBuiltinTheme());
  if (theme.id !== requested) setSetting("theme_active_id", theme.id);
  return theme;
}

function setActiveTheme(themeId) {
  const theme = getThemeById(themeId);
  if (!theme) throw new Error("Theme not found.");
  setSetting("theme_active_id", theme.id);
  return theme;
}

function cleanName(value) {
  const name = String(value || "").trim().replace(/\s+/g, " ");
  if (name.length < 2 || name.length > 60) {
    throw new Error("Theme name must be between 2 and 60 characters.");
  }
  return name;
}

function insertCustomTheme(name, baseThemeId, values) {
  const clean = cleanName(name);
  const base = getBuiltinTheme(baseThemeId);
  const normalized = normalizeThemeValues(values, base);
  const errors = validateThemeValues(normalized);
  if (errors.length) throw new Error(errors[0]);
  const id = crypto.randomUUID();
  const now = Date.now();
  db.prepare(
    "INSERT INTO custom_themes (id, name, base_theme_id, values_json, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)"
  ).run(id, clean, base.id, JSON.stringify(normalized), now, now);
  return getThemeById(customKey(id));
}

function duplicateTheme(themeId, name) {
  const source = getThemeById(themeId);
  if (!source) throw new Error("Theme not found.");
  return insertCustomTheme(
    name || `${source.name} Copy`,
    source.builtin ? source.id : source.baseThemeId,
    source
  );
}

function saveCustomTheme(themeId, values) {
  const id = customId(themeId);
  if (!id) throw new Error("Built-in themes are read-only.");
  const current = getThemeById(themeId);
  if (!current) throw new Error("Theme not found.");
  const normalized = normalizeThemeValues(values, getBuiltinTheme(current.baseThemeId));
  const errors = validateThemeValues(values);
  if (errors.length) {
    const error = new Error(errors[0]);
    error.validationErrors = errors;
    throw error;
  }
  db.prepare(
    "UPDATE custom_themes SET values_json = ?, updated_at = ? WHERE id = ?"
  ).run(JSON.stringify(normalized), Date.now(), id);
  return getThemeById(themeId);
}

function renameCustomTheme(themeId, name) {
  const id = customId(themeId);
  if (!id) throw new Error("Built-in themes cannot be renamed.");
  const result = db
    .prepare("UPDATE custom_themes SET name = ?, updated_at = ? WHERE id = ?")
    .run(cleanName(name), Date.now(), id);
  if (!result.changes) throw new Error("Theme not found.");
  return getThemeById(themeId);
}

function deleteCustomTheme(themeId) {
  const id = customId(themeId);
  if (!id) throw new Error("Built-in themes cannot be deleted.");
  const activeId = getSetting("theme_active_id", DEFAULT_THEME_ID);
  const result = db.prepare("DELETE FROM custom_themes WHERE id = ?").run(id);
  if (!result.changes) throw new Error("Theme not found.");
  if (activeId === themeId) setSetting("theme_active_id", DEFAULT_THEME_ID);
}

function valuesFromRequest(body, fallbackTheme = getBuiltinTheme()) {
  const values = { light: {}, dark: {}, role: {}, metrics: {}, typography: {} };
  for (const mode of ["light", "dark"]) {
    for (const field of MODE_COLOR_FIELDS) {
      values[mode][field] = String(
        body?.[`${mode}_${field}`] ?? fallbackTheme[mode][field]
      ).trim();
    }
  }
  for (const field of ROLE_COLOR_FIELDS) {
    values.role[field] = String(
      body?.[`role_${field}`] ?? fallbackTheme.role[field]
    ).trim();
  }
  values.metrics.radius = Number(body?.metrics_radius ?? fallbackTheme.metrics.radius);
  values.metrics.shadowStrength = Number(
    body?.metrics_shadowStrength ?? fallbackTheme.metrics.shadowStrength
  );
  values.metrics.spacingScale = Number(
    body?.metrics_spacingScale ?? fallbackTheme.metrics.spacingScale
  );
  values.typography.bodyFont = String(
    body?.typography_bodyFont ?? fallbackTheme.typography.bodyFont
  );
  values.typography.displayFont = String(
    body?.typography_displayFont ?? fallbackTheme.typography.displayFont
  );
  values.typography.monoFont = String(
    body?.typography_monoFont ?? fallbackTheme.typography.monoFont
  );
  values.typography.baseSize = Number(
    body?.typography_baseSize ?? fallbackTheme.typography.baseSize
  );
  values.typography.headingScale = Number(
    body?.typography_headingScale ?? fallbackTheme.typography.headingScale
  );
  values.typography.controlScale = Number(
    body?.typography_controlScale ?? fallbackTheme.typography.controlScale
  );
  return values;
}

module.exports = {
  BUILTIN_THEMES,
  DEFAULT_THEME_ID,
  FONT_STACKS,
  MODE_COLOR_FIELDS,
  ROLE_COLOR_FIELDS,
  TYPOGRAPHY_FIELDS,
  contrastRatio,
  deleteCustomTheme,
  duplicateTheme,
  getActiveTheme,
  getThemeById,
  listThemes,
  normalizeThemeValues,
  renameCustomTheme,
  saveCustomTheme,
  setActiveTheme,
  validateThemeValues,
  valuesFromRequest
};
