/* panels/notation.js — interactive variation tree.
   Mainline flows as paragraphs; variations are indented collapsible
   blocks. Right-click moves for promote/delete/annotate. Click to jump.
   Supports NAG glyphs, comments, quality colors, optional inline evals. */
'use strict';

import { h, clear, esc } from '../ui/dom.js';
import { icon } from '../ui/icons.js';
import { showMenu, promptDialog, makePanel } from '../ui/widgets.js';
import { NAG_TEXT } from '../core/doc.js';
import { figurine } from '../core/doc.js';
import { settings } from '../svc/settings.js';
import { fmtEval } from '../svc/engines.js';

const QUALITY_NAGS = [
  { nag: 3, label: '!! Brilliant' }, { nag: 1, label: '! Good move' },
  { nag: 5, label: '!? Interesting' }, { nag: 6, label: '?! Dubious' },
  { nag: 2, label: '? Mistake' }, { nag: 4, label: '?? Blunder' },
];
const EVAL_NAGS = [
  { nag: 10, label: '= Equal' }, { nag: 14, label: '⩲ White slightly better' },
  { nag: 15, label: '⩱ Black slightly better' }, { nag: 16, label: '± White better' },
  { nag: 17, label: '∓ Black better' }, { nag: 18, label: '+− White winning' },
  { nag: 19, label: '−+ Black winning' }, { nag: 13, label: '∞ Unclear' },
];

export function createNotationPanel(doc, { onNavigate, showEvals = false, title = 'Notation' } = {}) {
  let currentDoc = doc;
  let searchTerm = '';

  const bodyEl = h('div.nt-body', { role: 'tree' });
  const searchInput = h('input.input', {
    placeholder: 'Search moves…', style: { width: '110px', height: '20px', fontSize: '11px' },
    oninput: () => { searchTerm = searchInput.value.trim().toLowerCase(); render(); },
    onkeydown: e => { if (e.key === 'Enter') jumpToMatch(); e.stopPropagation(); },
  });

  const panel = makePanel({
    id: 'notation', title, icon: 'list',
    actions: [
      { icon: 'chevronU', title: 'Promote variation (of current move)', onClick: () => { currentDoc.promote(currentDoc.current); } },
      { icon: 'search', title: 'Search notation', onClick: () => { searchInput.style.display = searchInput.style.display === 'none' ? '' : 'none'; searchInput.focus(); } },
    ],
    body: bodyEl,
  });
  searchInput.style.display = 'none';
  panel.el.querySelector('.panel-actions').prepend(searchInput);

  function jumpToMatch() {
    if (!searchTerm) return;
    const flat = currentDoc.flatten();
    const start = flat.indexOf(currentDoc.current) + 1;
    for (let i = 0; i < flat.length; i++) {
      const n = flat[(start + i) % flat.length];
      if (n.san.toLowerCase().includes(searchTerm) || (n.comment || '').toLowerCase().includes(searchTerm)) {
        currentDoc.goTo(n);
        return;
      }
    }
  }

  function moveSpan(node) {
    const fig = settings.get('ui.figurine');
    const parts = [];
    if (currentDoc.isWhiteMove(node)) parts.push(currentDoc.moveNumberAt(node) + '.');
    else if (node === node.parent.children[0] ? node.parent === currentDoc.root || lineStart(node) : lineStart(node))
      parts.push(currentDoc.moveNumberAt(node) + '…');
    const nagStr = node.nags.filter(n => NAG_TEXT[n]).map(n => NAG_TEXT[n]).join('');
    const cls = ['nt-move'];
    if (node === currentDoc.current) cls.push('cur');
    if (node.quality) cls.push('q-' + node.quality);
    else {
      const qn = node.nags.find(n => n >= 1 && n <= 6);
      if (qn) cls.push('q-' + ({ 1: 'good', 2: 'mistake', 3: 'brilliant', 4: 'blunder', 5: 'interesting', 6: 'dubious' }[qn]));
    }
    if (searchTerm && node.san.toLowerCase().includes(searchTerm)) cls.push('hit');
    const el = h('span', {
      class: cls.join(' '), role: 'treeitem', tabindex: -1,
      dataset: { nodeId: node.id },
      onclick: () => { currentDoc.goTo(node); },
      oncontextmenu: e => { e.preventDefault(); e.stopPropagation(); showMoveMenu(node, e.clientX, e.clientY); },
    },
      parts.join(' ') + (parts.length ? ' ' : '') + figurine(node.san, fig) + nagStr);
    if (showEvals && (node.evalCp != null || node.evalMate != null)) {
      el.append(h('span.nt-eval', {},
        fmtEval(node.evalMate != null ? { mate: node.evalMate } : { cp: node.evalCp }, 'pawns')));
    }
    return el;
  }

  function lineStart(node) {
    // needs number: black move that begins a rendered segment
    const prev = node.parent;
    return prev === currentDoc.root || prev.children[0] !== node || !!prev.comment ||
      (prev.parent && prev.parent.children.length > 1 && prev.parent.children[0] === prev);
  }

  function commentSpan(node) {
    if (!node.comment) return null;
    const hit = searchTerm && node.comment.toLowerCase().includes(searchTerm);
    return h('span.nt-comment', {
      class: hit ? 'hit' : '',
      onclick: e => { e.stopPropagation(); editComment(node); },
      title: 'Click to edit comment',
    }, node.comment);
  }

  /** render the chain starting at `node` into container (inline flow) */
  function renderChain(node, container, depth) {
    while (node) {
      container.append(moveSpan(node), ' ');
      const c = commentSpan(node);
      if (c) container.append(c, ' ');
      const parent = node.parent;
      if (parent.children.length > 1 && node === parent.children[0]) {
        // variations of this move
        for (const alt of parent.children.slice(1)) {
          container.append(renderVariation(alt, depth + 1));
        }
      }
      node = node.children[0];
    }
  }

  function renderVariation(alt, depth) {
    const count = countNodes(alt);
    const collapsed = alt._collapsed;
    const block = h('div.nt-var', { style: { marginLeft: Math.min(depth, 5) * 14 + 'px' } });
    const toggle = h('span.nt-var-toggle', {
      html: icon(collapsed ? 'chevronR' : 'chevronD', 'ic-sm'),
      title: collapsed ? 'Expand variation' : 'Collapse variation',
      onclick: () => { alt._collapsed = !alt._collapsed; render(); },
    });
    block.append(toggle, '( ');
    if (collapsed) {
      block.append(moveSpan(alt), h('span.faint', {}, ` … ${count} moves )`));
    } else {
      renderChain(alt, block, depth);
      block.append(')');
    }
    return block;
  }

  function countNodes(n) {
    let c = 1;
    for (const k of n.children) c += countNodes(k);
    return c;
  }

  function render() {
    clear(bodyEl);
    if (!currentDoc.root.children.length) {
      bodyEl.append(h('div.nt-empty', {}, 'No moves yet.'));
      return;
    }
    if (currentDoc.root.comment) bodyEl.append(h('div.nt-comment.nt-precomment', {}, currentDoc.root.comment));
    const flow = h('div.nt-flow');
    renderChain(currentDoc.root.children[0], flow, 0);
    bodyEl.append(flow);
    const result = currentDoc.headers.Result;
    if (result && result !== '*') bodyEl.append(h('div.nt-result', {}, result));
    const cur = bodyEl.querySelector('.nt-move.cur');
    if (cur) cur.scrollIntoView({ block: 'nearest' });
  }

  async function editComment(node) {
    const text = await promptDialog({
      title: 'Comment', label: `After ${node.san}`, value: node.comment, multiline: true,
    });
    if (text !== null) currentDoc.setComment(node, text.trim());
  }

  function showMoveMenu(node, x, y) {
    const isVariation = node.parent.children[0] !== node ||
      currentDoc.path(node).some(n => n.parent.children[0] !== n);
    showMenu([
      { head: node.san },
      { label: 'Edit comment…', icon: 'pencil', action: () => editComment(node) },
      {
        label: 'Annotate', icon: 'star', action: () => {
          showMenu(
            [...QUALITY_NAGS, { sep: true }, ...EVAL_NAGS].map(q => q.sep ? q : ({
              label: q.label, checked: node.nags.includes(q.nag),
              action: () => currentDoc.toggleNag(node, q.nag),
            })), x + 10, y + 10);
        },
      },
      { sep: true },
      { label: 'Promote variation', icon: 'chevronU', disabled: node.parent.children[0] === node, action: () => currentDoc.promote(node) },
      { label: 'Make mainline', icon: 'zap', disabled: !isVariation, action: () => currentDoc.promoteToMain(node) },
      { sep: true },
      { label: 'Delete from here', icon: 'trash', danger: true, action: () => currentDoc.deleteNode(node) },
      { label: 'Truncate after this move', icon: 'close', disabled: !node.children.length, action: () => currentDoc.truncateAfter(node) },
      { sep: true },
      { label: 'Copy line as UCI', icon: 'copy', action: () => {
        const ucis = currentDoc.path(node).map(n => n.uci).join(' ');
        navigator.clipboard.writeText(ucis).catch(() => {});
      } },
    ], x, y);
  }

  let unsubs = [];
  function bind(newDoc) {
    unsubs.forEach(u => u());
    currentDoc = newDoc;
    unsubs = [
      currentDoc.on('change', render),
      currentDoc.on('navigate', () => { render(); onNavigate && onNavigate(); }),
    ];
    render();
  }
  bind(currentDoc);

  return { panel, render, bind, get doc() { return currentDoc; } };
}
