/* modes/study.js — Study mode: studies with (nested) chapters, each chapter
   a full variation tree with comments/annotations, embedded questions,
   bookmarks, tags, progress tracking, reveal-next-move and presentation
   modes, PGN import/export, automatic saving to IndexedDB. */
'use strict';

import { h, clear } from '../ui/dom.js';
import { icon } from '../ui/icons.js';
import { showDialog, promptDialog, confirmDialog, toast, makePanel, emptyState, attachContextMenu } from '../ui/widgets.js';
import { Dock, loadLayout, saveLayout } from '../ui/dock.js';
import { GameDoc, parsePgnGame, splitPgn } from '../core/doc.js';
import { idb } from '../svc/idb.js';
import { commands } from '../svc/commands.js';
import { BoardSession } from '../panels/session.js';
import { createNotationPanel } from '../panels/notation.js';
import { downloadText } from '../ui/appbus.js';

/* study: {id, name, tags:[], chapters:[{id, name, parentId|null, doc:serialized, done:bool, bookmarked:bool, notes:''}], created, updated} */

export const studyMode = {
  id: 'study', title: 'Study', icon: 'book', kbd: 'Ctrl+4',

  mount(root, ctx) {
    this.ctx = ctx;
    this.studies = [];
    this.study = null;
    this.chapter = null;
    this.doc = new GameDoc();
    this.reveal = false;         // reveal-next-move mode
    this._saveTimer = null;

    /* ---- studies & chapters panel ---- */
    this.treeEl = h('div', { style: { padding: '4px 0', overflowY: 'auto', height: '100%' } });
    const listPanel = makePanel({
      id: 'studylist', title: 'Studies', icon: 'book',
      actions: [
        { icon: 'plus', title: 'New study', onClick: () => this.newStudy() },
        { icon: 'upload', title: 'Import PGN as study', onClick: () => this.importStudy() },
      ],
      body: this.treeEl,
    });

    /* ---- notes panel ---- */
    this.notesArea = h('textarea.input', {
      rows: 6, placeholder: 'Private notes for this chapter…',
      style: { width: '100%', height: '100%', border: 'none', borderRadius: 0, background: 'var(--bg-inset)' },
      oninput: () => { if (this.chapter) { this.chapter.notes = this.notesArea.value; this.scheduleSave(); } },
    });
    const notesPanel = makePanel({ id: 'studynotes', title: 'Notes', icon: 'pencil', body: this.notesArea });

    /* ---- board & notation ---- */
    this.session = new BoardSession({
      doc: this.doc,
      userTurn: () => this.reveal ? null : 'both',
      onUserMove: uci => {
        const pos = this.doc.position;
        const m = pos.findUci(uci);
        if (m) { this.session.playSoundFor(m); this.doc.playMove(m); this.scheduleSave(); }
      },
    });
    this.notation = createNotationPanel(this.doc, {});

    this.toolbar = h('div.toolbar', {},
      this.chapterTitle = h('span.mode-toolbar-title', {}, 'No study open'),
      h('div.toolbar-sep'),
      this.btnReveal = h('button.btn.btn-sm', {
        html: icon('eye') + '<span>Reveal mode</span>',
        title: 'Hide the continuation; step through with the right arrow key',
        onclick: () => this.toggleReveal(),
      }),
      this.btnQuestion = h('button.btn.btn-sm', {
        html: icon('question') + '<span>Add question</span>',
        title: 'Embed a question at the current move',
        onclick: () => this.addQuestion(),
      }),
      this.btnDone = h('button.btn.btn-sm', { html: icon('check') + '<span>Mark done</span>', onclick: () => this.toggleDone() }),
      h('div.toolbar-sep'),
      h('button.btn.btn-ghost.btn-sm', { html: icon('download') + '<span>Export study</span>', onclick: () => this.exportStudy() }),
      h('button.btn.btn-ghost.btn-sm', { html: icon('microscope') + '<span>Analyse</span>', onclick: () => this.doc && this.ctx.openInAnalysis(this.doc) }),
      h('div.toolbar-spacer'),
      this.progressEl = h('span.mode-toolbar-title'),
    );

    const defaults = {
      left: { size: 240, panels: ['studylist'] },
      right: { size: 320, panels: ['notation', 'studynotes'] },
      bottom: { size: 140, panels: [] }, hidden: [],
    };
    this.dock = new Dock(root, {
      registry: {
        studylist: { title: 'Studies', icon: 'book', create: () => listPanel },
        notation: { title: 'Notation', icon: 'list', create: () => this.notation.panel },
        studynotes: { title: 'Notes', icon: 'pencil', create: () => notesPanel },
      },
      layout: loadLayout('study', defaults), defaults,
      onChange: l => saveLayout('study', l),
    });
    this.dock.centerEl.append(h('div.dock-col', { style: { flex: 1, height: '100%' } }, this.toolbar, this.session.el));
    this.dock.render();

    commands.registerAll([
      { id: 'study.new', title: 'New study', category: 'Study', icon: 'book', run: () => { ctx.switchMode('study'); this.newStudy(); } },
      { id: 'study.reveal', title: 'Toggle reveal-next-move mode', category: 'Study', icon: 'eye', run: () => this.toggleReveal() },
    ]);

    this._docUnsub = [];
    this.load();
  },

  activate() { this.session.update(false); },
  deactivate() { this.saveNow(); },
  getDoc() { return this.doc; },
  getSession() { return this.session; },
  toggleZen() { this.dock.setZen(!this.dock.zen); },

  /* ================= persistence ================= */
  async load() {
    this.studies = await idb.getAll('studies');
    this.paintTree();
    if (this.studies.length && !this.study) this.openStudy(this.studies[0]);
  },

  scheduleSave() {
    clearTimeout(this._saveTimer);
    this._saveTimer = setTimeout(() => this.saveNow(), 800);
  },
  async saveNow() {
    if (!this.study) return;
    if (this.chapter) this.chapter.doc = this.doc.serialize();
    this.study.updated = Date.now();
    await idb.put('studies', this.study);
    this.paintTree();
  },

  /* ================= tree ================= */
  paintTree() {
    clear(this.treeEl);
    if (!this.studies.length) {
      this.treeEl.append(emptyState({
        icon: 'book', title: 'No studies yet',
        hint: 'Create a study to organize openings, endgames or annotated games into chapters.',
        action: h('button.btn.btn-primary', { onclick: () => this.newStudy() }, 'New study'),
      }));
      return;
    }
    for (const st of this.studies) {
      const open = this.study && this.study.id === st.id;
      const row = h('div.list-row', {
        class: open ? 'sel' : '', style: { fontWeight: 600 },
        onclick: () => this.openStudy(st),
      },
        h('span', { html: icon(open ? 'chevronD' : 'chevronR', 'ic-sm') }),
        h('span.grow', { style: { overflow: 'hidden', textOverflow: 'ellipsis' } }, st.name),
        h('span.faint', {}, `${st.chapters.filter(c => c.done).length}/${st.chapters.length}`));
      attachContextMenu(row, () => [
        { label: 'Rename study…', icon: 'pencil', action: async () => {
          const name = await promptDialog({ title: 'Rename study', label: 'Name', value: st.name });
          if (name) { st.name = name; await idb.put('studies', st); this.paintTree(); }
        } },
        { label: 'Add chapter', icon: 'plus', action: () => this.addChapter(st) },
        { label: 'Export as PGN', icon: 'download', action: () => this.exportStudy(st) },
        { sep: true },
        { label: 'Delete study', icon: 'trash', danger: true, action: async () => {
          if (await confirmDialog({ title: 'Delete study', message: `Delete "${st.name}" and all its chapters?`, confirmLabel: 'Delete', danger: true })) {
            await idb.del('studies', st.id);
            if (this.study?.id === st.id) { this.study = null; this.chapter = null; }
            this.load();
          }
        } },
      ]);
      this.treeEl.append(row);
      if (open) {
        const byParent = pid => st.chapters.filter(c => (c.parentId || null) === pid);
        const renderLevel = (pid, depth) => {
          for (const ch of byParent(pid)) {
            const cur = this.chapter && this.chapter.id === ch.id;
            const chRow = h('div.list-row', {
              class: cur ? 'sel' : '',
              style: { paddingLeft: 26 + depth * 14 + 'px' },
              onclick: () => this.openChapter(ch),
            },
              h('span', { html: icon(ch.done ? 'check' : 'file', 'ic-sm'), style: { color: ch.done ? 'var(--ok-text)' : 'var(--text-faint)' } }),
              h('span.grow', { style: { overflow: 'hidden', textOverflow: 'ellipsis' } }, ch.name),
              ch.bookmarked ? h('span', { html: icon('star', 'ic-sm'), style: { color: 'var(--warn-text)' } }) : null);
            attachContextMenu(chRow, () => this.chapterMenu(st, ch));
            this.treeEl.append(chRow);
            renderLevel(ch.id, depth + 1);
          }
        };
        renderLevel(null, 0);
        this.treeEl.append(h('div.list-row', { style: { paddingLeft: '26px', color: 'var(--text-faint)' }, onclick: () => this.addChapter(st) },
          h('span', { html: icon('plus', 'ic-sm') }), 'Add chapter'));
      }
    }
  },

  chapterMenu(st, ch) {
    const idx = st.chapters.indexOf(ch);
    return [
      { label: 'Rename…', icon: 'pencil', action: async () => {
        const name = await promptDialog({ title: 'Rename chapter', label: 'Name', value: ch.name });
        if (name) { ch.name = name; await idb.put('studies', st); this.paintTree(); }
      } },
      { label: ch.bookmarked ? 'Remove bookmark' : 'Bookmark', icon: 'star', action: async () => { ch.bookmarked = !ch.bookmarked; await idb.put('studies', st); this.paintTree(); } },
      { label: 'Add sub-chapter', icon: 'plus', action: () => this.addChapter(st, ch.id) },
      { sep: true },
      { label: 'Move up', icon: 'chevronU', disabled: idx <= 0, action: async () => {
        st.chapters.splice(idx, 1); st.chapters.splice(idx - 1, 0, ch);
        await idb.put('studies', st); this.paintTree();
      } },
      { label: 'Move down', icon: 'chevronD', disabled: idx >= st.chapters.length - 1, action: async () => {
        st.chapters.splice(idx, 1); st.chapters.splice(idx + 1, 0, ch);
        await idb.put('studies', st); this.paintTree();
      } },
      { sep: true },
      { label: 'Delete chapter', icon: 'trash', danger: true, action: async () => {
        if (await confirmDialog({ title: 'Delete chapter', message: `Delete "${ch.name}"?`, confirmLabel: 'Delete', danger: true })) {
          st.chapters = st.chapters.filter(c => c.id !== ch.id && c.parentId !== ch.id);
          if (this.chapter?.id === ch.id) this.chapter = null;
          await idb.put('studies', st);
          this.paintTree();
        }
      } },
    ];
  },

  /* ================= studies ================= */
  async newStudy() {
    const name = await promptDialog({ title: 'New study', label: 'Study name', placeholder: 'e.g. Rook endgames' });
    if (!name || !name.trim()) return;
    const st = { name: name.trim(), tags: [], chapters: [], created: Date.now(), updated: Date.now() };
    st.id = await idb.add('studies', st);
    await idb.put('studies', st);
    this.studies.push(st);
    this.openStudy(st);
    this.addChapter(st);
  },

  async addChapter(st, parentId = null) {
    const name = await promptDialog({ title: 'New chapter', label: 'Chapter name', placeholder: 'e.g. Lucena position' });
    if (!name || !name.trim()) return;
    const ch = {
      id: Date.now() + Math.floor(Math.random() * 1000), parentId,
      name: name.trim(), doc: new GameDoc().serialize(), done: false, bookmarked: false, notes: '',
    };
    st.chapters.push(ch);
    await idb.put('studies', st);
    this.paintTree();
    this.openChapter(ch);
  },

  openStudy(st) {
    this.saveNow();
    this.study = st;
    this.chapter = null;
    this.paintTree();
    const first = st.chapters[0];
    if (first) this.openChapter(first);
    this.paintProgress();
  },

  openChapter(ch) {
    if (this.chapter && this.chapter !== ch) this.saveNow();
    this.chapter = ch;
    this.doc = GameDoc.deserialize(ch.doc);
    this._docUnsub.forEach(u => u());
    this._docUnsub = [
      this.doc.on('change', () => { this.scheduleSave(); this.checkQuestion(); }),
      this.doc.on('navigate', () => this.checkQuestion()),
    ];
    this.session.bindDoc(this.doc);
    this.notation.bind(this.doc);
    this.notesArea.value = ch.notes || '';
    this.chapterTitle.textContent = `${this.study.name} — ${ch.name}`;
    this.btnDone.innerHTML = icon(ch.done ? 'close' : 'check') + `<span>${ch.done ? 'Mark undone' : 'Mark done'}</span>`;
    this.paintTree();
    this.paintProgress();
  },

  paintProgress() {
    if (!this.study) { this.progressEl.textContent = ''; return; }
    const done = this.study.chapters.filter(c => c.done).length;
    this.progressEl.textContent = `Progress: ${done}/${this.study.chapters.length} chapters`;
  },

  async toggleDone() {
    if (!this.chapter) return;
    this.chapter.done = !this.chapter.done;
    await this.saveNow();
    this.btnDone.innerHTML = icon(this.chapter.done ? 'close' : 'check') + `<span>${this.chapter.done ? 'Mark undone' : 'Mark done'}</span>`;
    this.paintProgress();
  },

  /* ================= reveal & questions ================= */
  toggleReveal() {
    this.reveal = !this.reveal;
    this.btnReveal.classList.toggle('active', this.reveal);
    if (this.reveal) {
      this.doc.goStart();
      toast('Reveal mode: use the right arrow key to step through the chapter', 'info');
    }
    this.session.update(false);
  },

  addQuestion() {
    if (!this.chapter) return;
    const node = this.doc.current;
    promptDialog({ title: 'Embed question', label: 'Question shown when reaching this move', value: node.comment.startsWith('Q:') ? node.comment.slice(2).trim() : '' })
      .then(q => {
        if (q === null) return;
        this.doc.setComment(node, q.trim() ? 'Q: ' + q.trim() : '');
        this.scheduleSave();
      });
  },

  checkQuestion() {
    const node = this.doc.current;
    if (node.comment && node.comment.startsWith('Q:') && this.reveal) {
      showDialog({
        title: 'Question',
        body: h('div', { style: { maxWidth: '380px' } }, node.comment.slice(2).trim()),
        footer: [h('button.btn.btn-primary', { onclick: e => e.target.closest('.veil').remove() }, 'Continue')],
      });
    }
  },

  /* ================= io ================= */
  async importStudy() {
    const text = await promptDialog({ title: 'Import PGN as study', label: 'Paste PGN — each game becomes a chapter', multiline: true, confirmLabel: 'Import' });
    if (!text) return;
    try {
      const chunks = splitPgn(text);
      if (!chunks.length) throw new Error('no games found');
      const name = await promptDialog({ title: 'Study name', label: 'Name for the new study', value: 'Imported study' });
      if (!name) return;
      const st = { name, tags: [], chapters: [], created: Date.now(), updated: Date.now() };
      let ok = 0;
      for (const chunk of chunks.slice(0, 64)) {
        try {
          const doc = parsePgnGame(chunk);
          st.chapters.push({
            id: Date.now() + ok, parentId: null,
            name: doc.headers.White !== 'White' || doc.headers.Black !== 'Black'
              ? `${doc.headers.White} - ${doc.headers.Black}` : `Chapter ${ok + 1}`,
            doc: doc.serialize(), done: false, bookmarked: false, notes: '',
          });
          ok++;
        } catch { /* skip unreadable game */ }
      }
      if (!ok) throw new Error('no readable games');
      st.id = await idb.add('studies', st);
      await idb.put('studies', st);
      this.studies.push(st);
      this.openStudy(st);
      toast(`Study created with ${ok} chapters`, 'ok');
    } catch (e) { toast('Import failed: ' + e.message, 'err'); }
  },

  exportStudy(st) {
    st = st || this.study;
    if (!st) return;
    this.saveNow();
    let out = '';
    for (const ch of st.chapters) {
      const doc = GameDoc.deserialize(ch.doc);
      doc.headers.Event = st.name;
      doc.headers.White = ch.name;
      doc.headers.Black = '';
      out += doc.pgn() + '\n';
    }
    downloadText(st.name.replace(/[^\w-]+/g, '_') + '.pgn', out);
    toast('Study exported', 'ok');
  },
};
