/* app.js — application boot: frame (rail / titlebar / statusbar),
   mode registry & lifecycle, global commands, settings dialog,
   shortcut editor, engine status. */
'use strict';

import { h, clear } from './ui/dom.js';
import { icon } from './ui/icons.js';
import { showMenu, menuButton, showDialog, toast, confirmDialog } from './ui/widgets.js';
import { settings, SETTINGS_SCHEMA } from './svc/settings.js';
import { commands, comboOf } from './svc/commands.js';
import { engines } from './svc/engines.js';
import { pieceDefsSVG } from './board/pieces.js';
import { wireAppBus, downloadText } from './ui/appbus.js';

import { playMode } from './modes/play.js';
import { analysisMode } from './modes/analysis.js';
import { databaseMode } from './modes/database.js';
import { studyMode } from './modes/study.js';
import { trainingMode } from './modes/training.js';
import { tournamentMode } from './modes/tournament.js';
import { matchMode } from './modes/match.js';
import { editorMode } from './modes/editor.js';
import { prepMode } from './modes/prep.js';
import { broadcastMode } from './modes/broadcast.js';

document.body.insertAdjacentHTML('afterbegin', pieceDefsSVG());

/* ================= mode registry ================= */
/* Analysis first: it is the primary workspace and the default landing mode. */
const MODES = [analysisMode, playMode, databaseMode, studyMode, trainingMode,
  tournamentMode, matchMode, editorMode, prepMode, broadcastMode];

const mainEl = document.getElementById('main');
const railEl = document.getElementById('rail');
const state = {
  active: null,          // mode object
  roots: new Map(),      // id -> root element
  mounted: new Set(),
};

export const ctx = {
  switchMode,
  openInAnalysis(doc) {
    switchMode('analysis');
    analysisMode.openDoc(doc);
  },
  openInPlay(fen) {
    switchMode('play');
    playMode.setupFromFen && playMode.setupFromFen(fen);
  },
  toast,
};

function switchMode(id) {
  const mode = MODES.find(m => m.id === id);
  if (!mode || state.active === mode) return;
  if (state.active) {
    state.active.deactivate && state.active.deactivate();
    state.roots.get(state.active.id).style.display = 'none';
    railEl.querySelector(`[data-mode="${state.active.id}"]`)?.classList.remove('active');
  }
  let root = state.roots.get(id);
  if (!root) {
    root = h('div.mode-root', { dataset: { mode: id } });
    mainEl.append(root);
    state.roots.set(id, root);
  }
  root.style.display = '';
  if (!state.mounted.has(id)) {
    try { mode.mount(root, ctx); }
    catch (e) { console.error(e); root.append(h('div.empty-state', {}, `Failed to load ${mode.title}: ${e.message}`)); }
    state.mounted.add(id);
  }
  state.active = mode;
  railEl.querySelector(`[data-mode="${id}"]`)?.classList.add('active');
  mode.activate && mode.activate();
  localStorage.setItem('rc.mode', id);
  document.title = `${mode.title} — RushChess`;
}

/* ================= rail ================= */
function buildRail() {
  clear(railEl);
  for (const m of MODES) {
    railEl.append(h('button.rail-btn', {
      html: icon(m.icon), title: `${m.title} (${commands.kbdOf({ id: 'mode.' + m.id, kbd: m.kbd }) || m.kbd || ''})`,
      dataset: { mode: m.id }, 'aria-label': m.title,
      onclick: () => switchMode(m.id),
    }));
  }
  railEl.append(h('div.rail-spacer'));
  railEl.append(h('button.rail-btn', {
    html: icon('gear'), title: 'Settings (Ctrl+,)', 'aria-label': 'Settings',
    onclick: () => openSettings(),
  }));
}

/* ================= titlebar ================= */
const titlebar = document.getElementById('titlebar');
const titleCenter = h('div.titlebar-title');

function buildTitlebar() {
  clear(titlebar);
  const mb = h('div.menubar');
  const items = {
    File: () => [
      { label: 'New game…', icon: 'plus', kbd: 'Ctrl+N', action: () => commands.run('play.new') },
      { label: 'Open PGN / import…', icon: 'upload', kbd: 'Ctrl+O', action: () => commands.run('db.import') },
      { sep: true },
      { label: 'Copy FEN', icon: 'copy', kbd: 'Ctrl+Shift+F', action: () => commands.run('game.copyFen') },
      { label: 'Copy PGN', icon: 'copy', kbd: 'Ctrl+Shift+C', action: () => commands.run('game.copyPgn') },
      { label: 'Export PGN file', icon: 'download', action: () => commands.run('game.exportPgn') },
    ],
    View: () => [
      { label: 'Command palette', icon: 'search', kbd: 'Ctrl+K', action: () => commands.openPalette() },
      { sep: true },
      { label: 'Dark theme', checked: settings.get('ui.theme') === 'dark', action: () => settings.set('ui.theme', 'dark') },
      { label: 'Light theme', checked: settings.get('ui.theme') === 'light', action: () => settings.set('ui.theme', 'light') },
      { sep: true },
      ...['compact', 'normal', 'spacious'].map(d => ({
        label: 'Density: ' + d, checked: settings.get('ui.density') === d,
        action: () => settings.set('ui.density', d),
      })),
      { sep: true },
      { label: 'Flip board', icon: 'flip', kbd: 'F', action: () => commands.run('board.flip') },
      { label: 'Fullscreen board', icon: 'expand', kbd: 'Z', action: () => commands.run('board.zen') },
    ],
    Modes: () => MODES.map(m => ({
      label: m.title, icon: m.icon, kbd: commands.kbdOf({ id: 'mode.' + m.id, kbd: m.kbd }),
      checked: state.active === m, action: () => switchMode(m.id),
    })),
    Help: () => [
      { label: 'Keyboard shortcuts', icon: 'keyboard', action: () => openSettings('Shortcuts') },
      { label: 'About RushChess', icon: 'info', action: showAbout },
    ],
  };
  for (const [name, build] of Object.entries(items)) {
    const btn = h('button.menubar-item', {}, name);
    menuButton(btn, build);
    mb.append(btn);
  }
  titlebar.append(mb, titleCenter, h('div.titlebar-actions', {},
    h('button.btn.btn-sm', {
      html: icon('download') + '<span>Get the app</span>', title: 'Install RushChess on your computer',
      style: { marginRight: '4px' }, onclick: () => showDownload(),
    }),
    h('button.icon-btn', {
      html: icon(settings.get('ui.theme') === 'dark' ? 'sun' : 'moon'),
      title: 'Toggle theme', 'aria-label': 'Toggle theme',
      onclick: e => {
        settings.set('ui.theme', settings.get('ui.theme') === 'dark' ? 'light' : 'dark');
        e.currentTarget.innerHTML = icon(settings.get('ui.theme') === 'dark' ? 'sun' : 'moon');
      },
    }),
    h('button.icon-btn', { html: icon('search'), title: 'Command palette (Ctrl+K)', 'aria-label': 'Command palette', onclick: () => commands.openPalette() }),
  ));
}

/* ================= download / install ================= */
function showDownload() {
  const dl = (file, label, icn, note) => h('a.btn.btn-primary', {
    href: 'download/' + file, download: '', style: { textDecoration: 'none', justifyContent: 'flex-start' },
  }, icon(icn), h('span', {}, label), note ? h('span.faint', { style: { marginLeft: 'auto', fontWeight: '400' } }, note) : null);
  showDialog({
    title: 'Install RushChess on your computer',
    body: h('div', { style: { maxWidth: '460px', display: 'flex', flexDirection: 'column', gap: '12px' } },
      h('p.muted', {}, 'Run RushChess locally as a desktop app — full-strength engines, all modes, your games stored on your own machine. No install wizard: download, unzip, and double-click.'),
      h('div', { style: { display: 'flex', flexDirection: 'column', gap: '8px' } },
        dl('RushChess-Windows.zip', 'Download for Windows', 'download', 'ZIP'),
        dl('RushChess.exe', 'Standalone .exe only', 'file', 'needs the folder')),
      h('div', { style: { fontSize: 'var(--fs-12)', color: 'var(--text-secondary)', lineHeight: '1.6' } },
        h('b', {}, 'How to run:'),
        h('ol', { style: { margin: '4px 0 0 18px', display: 'flex', flexDirection: 'column', gap: '2px' } },
          h('li', {}, 'Download the Windows ZIP and extract it anywhere.'),
          h('li', {}, 'Double-click ', h('span.mono', {}, 'RushChess.exe'), ' — it opens in your browser.'),
          h('li', {}, 'That’s it. Includes the RushChess and Stockfish engines.'))),
      h('p.faint', { style: { fontSize: 'var(--fs-11)' } }, 'Windows 10/11, 64-bit. The app runs entirely on your computer; nothing is uploaded.')),
  });
}

function setTitle(text) { titleCenter.textContent = text || ''; }

/* ================= statusbar ================= */
const statusbar = document.getElementById('statusbar');
const sbMode = h('span.sb-item');
const sbEngines = h('span.sb-item.sb-click', { onclick: () => engines.refresh().then(paintStatus) });
const sbCustom = h('span.sb-item.grow', { style: { overflow: 'hidden', textOverflow: 'ellipsis' } });

function buildStatusbar() {
  statusbar.append(sbMode, sbCustom, h('span.sb-spacer'), sbEngines,
    h('span.sb-item.sb-click', { onclick: () => commands.openPalette(), html: icon('keyboard') + '<span>Ctrl+K</span>' }));
}
function paintStatus() {
  const av = engines.available();
  sbEngines.innerHTML = av.length
    ? `<span class="sb-ok">●</span> ${av.map(e => e.name).join(' · ')}`
    : `<span class="sb-err">●</span> engines offline`;
  sbEngines.title = av.length ? 'Engines connected' : 'Start server.py to enable engines';
  sbMode.textContent = state.active ? state.active.title : '';
}
function setStatusText(text) { sbCustom.textContent = text || ''; }
wireAppBus({ onStatusText: setStatusText, onTitle: setTitle });

/* ================= settings dialog ================= */
let settingsDlg = null;
export function openSettings(category) {
  if (settingsDlg) { settingsDlg.close(); settingsDlg = null; }
  const cats = [...Object.keys(SETTINGS_SCHEMA), 'Shortcuts'];
  let current = category || cats[0];
  let query = '';

  const nav = h('div.settings-nav');
  const content = h('div.settings-content');
  const search = h('input.input', {
    placeholder: 'Search settings…', style: { width: '100%', marginBottom: '6px' },
    oninput: () => { query = search.value.trim().toLowerCase(); paint(); },
  });

  function paintNav() {
    clear(nav);
    nav.append(search);
    for (const c of cats) {
      nav.append(h('div.list-row', {
        class: c === current && !query ? 'sel' : '',
        onclick: () => { current = c; query = ''; search.value = ''; paint(); },
      }, c));
    }
  }

  function settingRow(item) {
    const val = settings.get(item.key);
    let ctl;
    if (item.type === 'bool') {
      ctl = h('input', { type: 'checkbox', checked: !!val, style: { accentColor: 'var(--accent)', width: '15px', height: '15px', cursor: 'pointer' },
        onchange: e => settings.set(item.key, e.target.checked) });
    } else if (item.type === 'select') {
      ctl = h('select.select', { onchange: e => settings.set(item.key, e.target.value) },
        ...item.options.map(o => h('option', { value: o, selected: o === val }, o)));
    } else if (item.type === 'number') {
      ctl = h('input.input', { type: 'number', value: val, min: item.min, max: item.max, style: { width: '80px' },
        onchange: e => settings.set(item.key, Math.max(item.min ?? -1e9, Math.min(item.max ?? 1e9, +e.target.value || 0))) });
    } else if (item.type === 'range') {
      const label = h('span.tnum.muted', { style: { minWidth: '34px', textAlign: 'right' } }, String(val));
      ctl = h('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', width: '170px' } },
        h('input.range', { type: 'range', min: item.min, max: item.max, step: item.step || 1, value: val,
          oninput: e => { settings.set(item.key, +e.target.value); label.textContent = e.target.value; } }),
        label);
    } else {
      ctl = h('input.input', { value: val ?? '', onchange: e => settings.set(item.key, e.target.value) });
    }
    return h('div.setting-row', {},
      h('div.sr-label', {}, item.label, item.hint ? h('div.sr-hint', {}, item.hint) : null),
      h('div.sr-ctl', {}, ctl));
  }

  function paintContent() {
    clear(content);
    if (query) {
      let found = 0;
      for (const [cat, items] of Object.entries(SETTINGS_SCHEMA)) {
        const matches = items.filter(i => i.label.toLowerCase().includes(query) || i.key.toLowerCase().includes(query));
        if (!matches.length) continue;
        content.append(h('div.settings-group-title', {}, cat));
        matches.forEach(i => content.append(settingRow(i)));
        found += matches.length;
      }
      if (!found) content.append(h('div.empty-state', {}, h('div.es-title', {}, 'No matching settings')));
      return;
    }
    if (current === 'Shortcuts') return paintShortcuts(content);
    content.append(h('div.settings-group-title', {}, current));
    (SETTINGS_SCHEMA[current] || []).forEach(i => content.append(settingRow(i)));
    if (current === 'General') {
      content.append(h('div', { style: { marginTop: '14px' } },
        h('button.btn', {
          onclick: async () => {
            if (await confirmDialog({ title: 'Reset settings', message: 'Restore every setting to its default value?', confirmLabel: 'Reset', danger: true }))
              { settings.resetAll(); paint(); }
          },
        }, 'Reset all settings to defaults')));
    }
  }

  function paintShortcuts(root) {
    root.append(h('div.settings-group-title', {}, 'Keyboard shortcuts'),
      h('div.sr-hint', { style: { marginBottom: '8px' } }, 'Click a binding, then press the new key combination. Esc cancels, Backspace clears.'));
    for (const c of commands.list().sort((a, b) => (a.category || '').localeCompare(b.category || '') || a.title.localeCompare(b.title))) {
      if (c.hidden) continue;
      const kbdBtn = h('button.btn.btn-sm.mono', {
        style: { minWidth: '90px' },
        onclick: () => {
          kbdBtn.textContent = 'Press keys…';
          kbdBtn.classList.add('kbd-capture');
          const onKey = e => {
            e.preventDefault(); e.stopPropagation();
            if (e.key === 'Escape') { done(commands.kbdOf(c)); return; }
            if (e.key === 'Backspace') { commands.setShortcut(c.id, null); done(null); return; }
            const combo = comboOf(e);
            if (!combo) return;
            commands.setShortcut(c.id, combo);
            done(combo);
          };
          const done = val => {
            document.removeEventListener('keydown', onKey, true);
            kbdBtn.classList.remove('kbd-capture');
            kbdBtn.textContent = val || 'unbound';
          };
          document.addEventListener('keydown', onKey, true);
        },
      }, commands.kbdOf(c) || 'unbound');
      root.append(h('div.setting-row', {},
        h('div.sr-label', {}, c.title, h('div.sr-hint', {}, c.category || '')),
        h('div.sr-ctl', {}, kbdBtn)));
    }
    root.append(h('div', { style: { marginTop: '12px' } },
      h('button.btn', { onclick: () => { commands.resetShortcuts(); paint(); } }, 'Reset all shortcuts')));
  }

  function paint() { paintNav(); paintContent(); }

  const layout = h('div.settings-layout', {}, nav, content);
  settingsDlg = showDialog({ title: 'Settings', body: layout, width: 'min(780px, 94vw)', onClose: () => { settingsDlg = null; } });
  paint();
}

function showAbout() {
  showDialog({
    title: 'About RushChess',
    body: h('div', { style: { maxWidth: '420px', lineHeight: '1.6' } },
      h('p', {}, h('b', {}, 'RushChess'), ' — a local chess workbench.'),
      h('p.muted', { style: { marginTop: '8px' } },
        'Engines: RushChess 1.0 (written in the Rush language, ~2700 Elo) and Stockfish 17.1. ',
        'All data stays on this machine (browser storage). No dependencies, no build step.'),
      h('p.muted', { style: { marginTop: '8px' } }, 'Move generation validated against published perft counts.')),
  });
}

/* ================= global commands ================= */
function registerGlobalCommands() {
  commands.registerAll([
    { id: 'app.palette', title: 'Command palette', category: 'App', icon: 'search', kbd: 'Ctrl+K', run: () => commands.openPalette() },
    { id: 'app.settings', title: 'Open settings', category: 'App', icon: 'gear', kbd: 'Ctrl+,', run: () => openSettings() },
    { id: 'app.theme', title: 'Toggle dark/light theme', category: 'App', icon: 'sun', run: () => settings.set('ui.theme', settings.get('ui.theme') === 'dark' ? 'light' : 'dark') },
    { id: 'app.shortcuts', title: 'Keyboard shortcuts', category: 'App', icon: 'keyboard', run: () => openSettings('Shortcuts') },
    ...MODES.map((m, i) => ({
      id: 'mode.' + m.id, title: 'Go to ' + m.title, category: 'Modes', icon: m.icon,
      kbd: 'Ctrl+' + ((i + 1) % 10), run: () => switchMode(m.id),
    })),
    // navigation — delegated to the active mode's doc/session
    { id: 'nav.prev', title: 'Previous move', category: 'Navigate', icon: 'chevronL', kbd: 'ArrowLeft', run: () => activeDoc()?.goPrev() },
    { id: 'nav.next', title: 'Next move', category: 'Navigate', icon: 'chevronR', kbd: 'ArrowRight', run: () => activeDoc()?.goNext() },
    { id: 'nav.start', title: 'Go to start', category: 'Navigate', icon: 'skipStart', kbd: 'Home', run: () => activeDoc()?.goStart() },
    { id: 'nav.end', title: 'Go to end', category: 'Navigate', icon: 'skipEnd', kbd: 'End', run: () => activeDoc()?.goEnd() },
    { id: 'nav.branch', title: 'Next branch/comment', category: 'Navigate', icon: 'tree', run: () => activeDoc()?.goNextBranch() },
    { id: 'board.flip', title: 'Flip board', category: 'Board', icon: 'flip', kbd: 'F', run: () => activeSession()?.board.flip() || activeSessionUpdate() },
    { id: 'board.blindfold', title: 'Toggle blindfold', category: 'Board', icon: 'eyeOff', run: () => settings.set('pieces.blindfold', !settings.get('pieces.blindfold')) },
    { id: 'board.zen', title: 'Fullscreen board (zen)', category: 'Board', icon: 'expand', kbd: 'Z', run: () => state.active?.toggleZen && state.active.toggleZen() },
    { id: 'game.copyFen', title: 'Copy FEN', category: 'Game', icon: 'copy', kbd: 'Ctrl+Shift+F', run: () => {
      const d = activeDoc(); if (!d) return toast('No active game', 'warn');
      navigator.clipboard.writeText(d.position.fen()).then(() => toast('FEN copied', 'ok'), () => toast('Copy failed', 'err'));
    } },
    { id: 'game.copyPgn', title: 'Copy PGN', category: 'Game', icon: 'copy', kbd: 'Ctrl+Shift+C', run: () => {
      const d = activeDoc(); if (!d) return toast('No active game', 'warn');
      navigator.clipboard.writeText(d.pgn()).then(() => toast('PGN copied', 'ok'), () => toast('Copy failed', 'err'));
    } },
    { id: 'game.exportPgn', title: 'Export game as PGN file', category: 'Game', icon: 'download', run: () => {
      const d = activeDoc(); if (!d) return toast('No active game', 'warn');
      downloadText((d.headers.White + '_vs_' + d.headers.Black).replace(/[^\w-]+/g, '_') + '.pgn', d.pgn());
    } },
  ]);
  commands.bindKeys();
}

function activeDoc() { return state.active?.getDoc ? state.active.getDoc() : null; }
function activeSession() { return state.active?.getSession ? state.active.getSession() : null; }
function activeSessionUpdate() { const s = activeSession(); s && s.update(false); }

/* ================= boot ================= */
buildRail();
buildTitlebar();
buildStatusbar();
registerGlobalCommands();

engines.on('status', paintStatus);
engines.refresh().then(() => {
  paintStatus();
  engines.startPolling();
});

switchMode(localStorage.getItem('rc.mode') || 'analysis');
paintStatus();
