/* settings.js — central settings store.
   Single source of truth; schema with categories drives the Settings UI.
   Persisted to localStorage. Migrates legacy keys (rc-theme, rc-sound). */
'use strict';

import { Emitter } from '../ui/dom.js';

const LS_KEY = 'rc.settings.v1';

/* Schema: category -> [{key, label, type, def, options?, min?, max?, step?, hint?}]
   type: bool | select | number | range | text | keys */
export const SETTINGS_SCHEMA = {
  General: [
    { key: 'general.confirmResign', label: 'Confirm before resigning', type: 'bool', def: true },
    { key: 'general.confirmNewGame', label: 'Confirm abandoning an unfinished game', type: 'bool', def: true },
    { key: 'general.autosaveGames', label: 'Save finished games to the Local database', type: 'bool', def: true },
    { key: 'general.openingNames', label: 'Show opening names', type: 'bool', def: true },
  ],
  Appearance: [
    { key: 'ui.theme', label: 'Theme', type: 'select', def: 'dark', options: ['dark', 'light'] },
    { key: 'ui.density', label: 'Interface density', type: 'select', def: 'normal', options: ['compact', 'normal', 'spacious'] },
    { key: 'ui.figurine', label: 'Figurine notation (♞f3 instead of Nf3)', type: 'bool', def: false },
  ],
  Board: [
    { key: 'board.theme', label: 'Board theme', type: 'select', def: 'walnut', options: ['walnut', 'slate', 'emerald', 'ivory', 'tournament'] },
    { key: 'board.coords', label: 'Coordinates', type: 'select', def: 'inside', options: ['inside', 'outside', 'off'] },
    { key: 'board.legalHints', label: 'Show legal move targets', type: 'bool', def: true },
    { key: 'board.lastMove', label: 'Highlight last move', type: 'bool', def: true },
    { key: 'board.checkHighlight', label: 'Highlight checks', type: 'bool', def: true },
    { key: 'board.animMs', label: 'Animation speed (ms, 0 = off)', type: 'range', def: 120, min: 0, max: 400, step: 20 },
    { key: 'board.evalBar', label: 'Show evaluation bar', type: 'bool', def: true },
    { key: 'board.zoom', label: 'Board size (% of available space)', type: 'range', def: 100, min: 50, max: 100, step: 5 },
  ],
  Pieces: [
    { key: 'pieces.set', label: 'Piece set', type: 'select', def: 'clean',
      options: ['clean', 'neo', 'classic', 'cburnett', 'merida', 'minimal', 'letters'],
      hint: 'Clean = your imported PNG set; Neo = flat modern; cburnett = lichess; Merida = classic print' },
    { key: 'pieces.blindfold', label: 'Blindfold mode (hide pieces)', type: 'bool', def: false },
  ],
  'Moves & Input': [
    { key: 'input.premove', label: 'Allow premoves', type: 'bool', def: true },
    { key: 'input.autoQueen', label: 'Always promote to queen', type: 'bool', def: false },
    { key: 'input.moveConfirm', label: 'Confirm each move (correspondence style)', type: 'bool', def: false },
    { key: 'input.keyboardEntry', label: 'Keyboard move entry (type SAN)', type: 'bool', def: true },
  ],
  Sound: [
    { key: 'sound.enabled', label: 'Sounds', type: 'bool', def: true },
    { key: 'sound.theme', label: 'Sound style', type: 'select', def: 'neo',
      options: ['neo', 'standard', 'wooden', 'digital'],
      hint: 'Neo ≈ chess.com click; wooden ≈ physical board thock; digital ≈ app blips' },
    { key: 'sound.volume', label: 'Volume', type: 'range', def: 70, min: 0, max: 100, step: 10 },
    { key: 'sound.lowTime', label: 'Low-time warning', type: 'bool', def: true },
  ],
  Engines: [
    { key: 'engine.sfThreads', label: 'Stockfish threads', type: 'number', def: 4, min: 1, max: 16 },
    { key: 'engine.sfHash', label: 'Stockfish hash (MB)', type: 'number', def: 256, min: 16, max: 2048 },
    { key: 'engine.minMoveMs', label: 'Minimum engine reply delay (ms)', type: 'number', def: 350, min: 0, max: 2000 },
  ],
  Analysis: [
    { key: 'analysis.autoAnalyse', label: 'Start live engine automatically in Analysis', type: 'bool', def: true,
      hint: 'Keeps the evaluation bar and graph live as you move through the game' },
    { key: 'analysis.multipv', label: 'Analysis lines (MultiPV)', type: 'number', def: 3, min: 1, max: 5 },
    { key: 'analysis.evalDisplay', label: 'Evaluation display', type: 'select', def: 'pawns', options: ['pawns', 'winpercent', 'wdl'] },
    { key: 'analysis.arrows', label: 'Best-move arrows', type: 'bool', def: true },
    { key: 'analysis.stepMs', label: 'Analysis first-pass time (ms)', type: 'number', def: 800, min: 200, max: 5000,
      hint: 'Time for the first pass; each pass then ramps up to the max below to reach deeper' },
    { key: 'analysis.maxStepMs', label: 'Analysis max time per pass (ms)', type: 'number', def: 8000, min: 1000, max: 30000,
      hint: 'How deep live analysis is allowed to climb — higher = deeper but slower to refresh' },
    { key: 'analysis.blunderCp', label: 'Blunder threshold (centipawns)', type: 'number', def: 200, min: 50, max: 900 },
    { key: 'analysis.mistakeCp', label: 'Mistake threshold (centipawns)', type: 'number', def: 90, min: 30, max: 500 },
    { key: 'analysis.reviewMs', label: 'Game review: time per move (ms)', type: 'number', def: 600, min: 100, max: 5000 },
  ],
  Training: [
    { key: 'training.hints', label: 'Allow hints', type: 'bool', def: true },
    { key: 'training.autoNext', label: 'Advance to next exercise automatically', type: 'bool', def: true },
  ],
  Performance: [
    { key: 'perf.explorerPlies', label: 'Explorer index depth (plies per game)', type: 'number', def: 24, min: 8, max: 60 },
    { key: 'perf.dbPageSize', label: 'Database rows fetched per page', type: 'number', def: 500, min: 100, max: 5000 },
  ],
};

export const SETTINGS_DEFAULTS = {};
for (const items of Object.values(SETTINGS_SCHEMA))
  for (const it of items) SETTINGS_DEFAULTS[it.key] = it.def;

class SettingsStore extends Emitter {
  constructor() {
    super();
    this.values = { ...SETTINGS_DEFAULTS };
    this._load();
    this._applyDocument();
  }

  _load() {
    try {
      const raw = localStorage.getItem(LS_KEY);
      if (raw) {
        Object.assign(this.values, JSON.parse(raw));
        return;
      }
      // ---- migration from legacy GUI (v0) ----
      const legacyTheme = localStorage.getItem('rc-theme');
      const legacySound = localStorage.getItem('rc-sound');
      if (legacyTheme != null) {
        const map = ['walnut', 'ivory', 'emerald', 'slate', 'tournament'];
        this.values['board.theme'] = map[+legacyTheme] || 'walnut';
      }
      if (legacySound != null) this.values['sound.enabled'] = legacySound !== '0';
      if (legacyTheme != null || legacySound != null) this._save();
    } catch (e) { console.error('settings load failed', e); }
  }

  _save() {
    // persist only non-default values to keep the blob small
    const diff = {};
    for (const [k, v] of Object.entries(this.values))
      if (JSON.stringify(v) !== JSON.stringify(SETTINGS_DEFAULTS[k])) diff[k] = v;
    localStorage.setItem(LS_KEY, JSON.stringify(diff));
  }

  get(key) { return this.values[key] ?? SETTINGS_DEFAULTS[key]; }

  set(key, value) {
    if (this.values[key] === value) return;
    this.values[key] = value;
    this._save();
    this._applyDocument(key);
    this.emit('change', key, value);
    this.emit('change:' + key, value);
  }

  reset(key) { this.set(key, SETTINGS_DEFAULTS[key]); }
  resetAll() {
    this.values = { ...SETTINGS_DEFAULTS };
    this._save();
    this._applyDocument();
    this.emit('change', '*');
  }

  _applyDocument(key) {
    const root = document.documentElement;
    if (!key || key === 'ui.theme') root.dataset.theme = this.get('ui.theme');
    if (!key || key === 'ui.density') root.dataset.density = this.get('ui.density');
    if (!key || key === 'board.theme') {
      const t = BOARD_THEMES[this.get('board.theme')] || BOARD_THEMES.walnut;
      root.style.setProperty('--sq-light', t.light);
      root.style.setProperty('--sq-dark', t.dark);
    }
  }
}

export const BOARD_THEMES = {
  walnut:     { label: 'Walnut',     light: '#e3d3b5', dark: '#a8845f' },
  slate:      { label: 'Slate',      light: '#cdd3dd', dark: '#7b869c' },
  emerald:    { label: 'Emerald',    light: '#e2e8d4', dark: '#739066' },
  ivory:      { label: 'Ivory',      light: '#f0e9dc', dark: '#b8a186' },
  tournament: { label: 'Tournament', light: '#e8e6e1', dark: '#4f7a55' },
};

export const settings = new SettingsStore();
