// ── Theme presets & the closed sets an admin may choose from ───────────────
//
// The single authority for admin-configurable theming (docs/website/THEMING_AND_NAV.md
// §5-§6). Everything an admin can pick is enumerated here; nothing is free text.
//
// Why the server owns this rather than theme.css:
//   The effective token set is resolved server-side and returned by
//   settings.getPublic() as `theme`, which the SPA writes onto the document as
//   CSS custom properties. That keeps ONE authority for the override merge
//   (:root ← preset ← custom), lets brand.accent — a cross-repo contract the
//   Android app themes itself from — report the same accent the website paints,
//   and avoids the precedence trap of `[data-theme]` blocks losing to the inline
//   `--accent` SiteContext already sets on <html>.
//
//   theme.css's `:root` remains the default and is NOT duplicated here beyond
//   the runic-gateway preset. An instance with no `theme_visual` row gets no
//   `theme` block at all and renders from :root exactly as it does today.
//
// Security note: these values end up as CSS custom property values. Every one is
// picked from a closed set (a preset id, a shortlist stack, a bounded px length,
// a hex color) — see utils/themeResolve.js, which both the write path and the
// read path validate through.

// The three color tokens that are semantic rather than decorative. They mean
// "live" and "maintenance" and stay fixed across every preset — green is not a
// brand choice. Deliberately absent from every preset block below.
const FIXED_TOKENS = ['--mode-live', '--mode-maint']

// Full palettes. A preset must carry EVERY color token, not just the eight the
// admin form exposes: a partial palette leaves e.g. --line and --blue at their
// dark-blue :root values, which reads as broken on a warm background.
//
// --panel-grad is deliberately absent: it is derived (`linear-gradient(180deg,
// var(--panel-a), var(--panel-b))`) and must stay derived, or a future light
// preset silently inherits a dark gradient.
const PRESETS = {
  // Today's :root, verbatim. Declared as a preset so that switching back to it
  // after trying another is the same code path as any other choice.
  'runic-gateway': {
    label: 'Runic Gateway',
    tokens: {
      '--bg': '#0e1318',
      '--bg-deep': '#0b0f14',
      '--panel-a': '#192231',
      '--panel-b': '#141a21',
      '--panel-flat': '#11161d',
      '--line': '#2a3544',
      '--line-soft': '#1d2733',
      '--accent': '#7f99bd',
      '--accent-bright': '#cdd9e8',
      '--ink': '#eef3f8',
      '--head': '#e6edf6',
      '--text': '#c4cdd8',
      '--muted': '#aeb8c4',
      '--dim': '#6f7d8e',
      '--blue': '#13243c',
      '--radius-pill': '999px',
      '--radius-panel': '12px',
      '--radius-card': '10px',
      '--radius-input': '8px',
      '--shadow-card': '0 14px 34px rgba(0, 0, 0, 0.3)',
      '--serif': 'Georgia, "Times New Roman", serif',
      '--display': 'Cinzel, Georgia, serif',
      '--sans': '"Helvetica Neue", Arial, sans-serif',
    },
  },
  // Flatter, cooler, sans-heavy. Reads as a SaaS dashboard, not fantasy.
  modern: {
    label: 'Modern',
    tokens: {
      '--bg': '#101114',
      '--bg-deep': '#0a0a0c',
      '--panel-a': '#1c1d22',
      '--panel-b': '#17181c',
      '--panel-flat': '#141519',
      '--line': '#2b2d34',
      '--line-soft': '#212329',
      '--accent': '#4f8ef7',
      '--accent-bright': '#a8c8ff',
      '--ink': '#f2f3f5',
      '--head': '#f7f8fa',
      '--text': '#b8bcc4',
      '--muted': '#a9aeb8',
      '--dim': '#71767f',
      '--blue': '#1b2c47',
      '--radius-pill': '8px',
      '--radius-panel': '8px',
      '--radius-card': '6px',
      '--radius-input': '6px',
      '--shadow-card': '0 8px 20px rgba(0, 0, 0, 0.25)',
      '--serif': 'Inter, Arial, sans-serif',
      '--display': "'Work Sans', Arial, sans-serif",
      '--sans': 'Inter, Arial, sans-serif',
    },
  },
  // Warmer, higher contrast, carved corners; leans into UO harder.
  fantasy: {
    label: 'Fantasy',
    tokens: {
      '--bg': '#1a120b',
      '--bg-deep': '#120c07',
      '--panel-a': '#2c1f14',
      '--panel-b': '#241a10',
      '--panel-flat': '#1f160d',
      '--line': '#4a3721',
      '--line-soft': '#33251a',
      '--accent': '#c9973f',
      '--accent-bright': '#e8c374',
      '--ink': '#f3e8d4',
      '--head': '#f7efe0',
      '--text': '#d3bfa0',
      '--muted': '#bfa985',
      '--dim': '#8a7454',
      '--blue': '#382613',
      '--radius-pill': '4px',
      '--radius-panel': '3px',
      '--radius-card': '2px',
      '--radius-input': '2px',
      '--shadow-card': '0 16px 38px rgba(0, 0, 0, 0.45)',
      '--serif': "'EB Garamond', Georgia, serif",
      '--display': 'Cinzel, Georgia, serif',
      '--sans': "'EB Garamond', Georgia, serif",
    },
  },
}

// 'custom' is a valid stored preset meaning "no preset base" — :root plus
// whatever custom fields are set. It has no palette of its own.
const CUSTOM_PRESET = 'custom'
const PRESET_IDS = [...Object.keys(PRESETS), CUSTOM_PRESET]

// The colors the admin form exposes, mapped to their CSS token. Deliberately
// the eight of §6.1 rather than all fifteen: the rest are supporting shades a
// preset sets coherently but that are not worth (or safe to) hand-picking.
const COLOR_FIELDS = {
  bg: '--bg',
  bgDeep: '--bg-deep',
  panelA: '--panel-a',
  panelB: '--panel-b',
  accent: '--accent',
  accentBright: '--accent-bright',
  ink: '--ink',
  text: '--text',
}

const RADIUS_FIELDS = {
  radiusPill: '--radius-pill',
  radiusPanel: '--radius-panel',
  radiusCard: '--radius-card',
  radiusInput: '--radius-input',
}

const FONT_FIELDS = {
  serif: '--serif',
  display: '--display',
  sans: '--sans',
}

// The curated Google Fonts shortlist (§5.1). The dropdown's VALUE is the full
// stack exactly as applied, so no string is ever built from admin input and no
// Google Fonts URL is ever assembled at runtime — the combined css2? request in
// client/index.html is static and covers all eight web families.
//
// One addition to §5.1's twelve: Georgia in the serif list. The shortlist as
// drafted gave the sans role a "current default" option (Arial, byte-identical
// to today's --sans) but left the serif role with no way back to today's
// `Georgia, "Times New Roman", serif` short of resetting the whole theme. It
// pulls in no web family, so §5.2's URL is unchanged.
const FONT_OPTIONS = {
  serif: [
    { value: "'EB Garamond', Georgia, serif", label: 'EB Garamond — strongest fantasy/historic' },
    { value: 'Merriweather, Georgia, serif', label: 'Merriweather — excellent readability' },
    { value: "'Playfair Display', Georgia, serif", label: 'Playfair Display — elegant/editorial' },
    { value: "'IM Fell English', Georgia, serif", label: 'IM Fell English — old-world (no bold weight)' },
    { value: 'Georgia, "Times New Roman", serif', label: 'Georgia — the shipped default' },
  ],
  display: [
    { value: 'Cinzel, Georgia, serif', label: 'Cinzel — current Runic Gateway identity' },
    { value: "'Playfair Display', Georgia, serif", label: 'Playfair Display — elegant alternative' },
    { value: "'EB Garamond', Georgia, serif", label: 'EB Garamond — softer/classic' },
    { value: "'IM Fell English', Georgia, serif", label: 'IM Fell English — very strong fantasy (no bold weight)' },
  ],
  sans: [
    { value: 'Inter, Arial, sans-serif', label: 'Inter — default modern UI choice' },
    { value: "'Work Sans', Arial, sans-serif", label: 'Work Sans — slightly more character' },
    { value: "'Source Sans 3', Arial, sans-serif", label: 'Source Sans 3 — extremely readable' },
    { value: '"Helvetica Neue", Arial, sans-serif', label: 'Arial — no webfont; the shipped default' },
  ],
}

// Shadow depth, as a closed set for the same reason fonts are: the stored value
// is applied verbatim as --shadow-card.
const SHADOW_OPTIONS = [
  { value: 'none', label: 'None — flat' },
  { value: '0 8px 20px rgba(0, 0, 0, 0.25)', label: 'Soft' },
  { value: '0 14px 34px rgba(0, 0, 0, 0.3)', label: 'Default' },
  { value: '0 18px 44px rgba(0, 0, 0, 0.45)', label: 'Deep' },
]

// Corner radius is a number, not a shortlist, so it is bounded instead: an
// integer count of px from 0 to 999 (999 being the pill).
const RADIUS_MAX_PX = 999

module.exports = {
  PRESETS,
  PRESET_IDS,
  CUSTOM_PRESET,
  FIXED_TOKENS,
  COLOR_FIELDS,
  RADIUS_FIELDS,
  FONT_FIELDS,
  FONT_OPTIONS,
  SHADOW_OPTIONS,
  RADIUS_MAX_PX,
}
