/* panels/evalgraph.js — evaluation graph over the mainline.
   Canvas rendering, click to navigate, marker for the current move,
   quality dots (mistakes/blunders) when a review has run. */
'use strict';

import { h } from '../ui/dom.js';
import { makePanel } from '../ui/widgets.js';
import { winPercent } from '../svc/engines.js';

export function createEvalGraph(doc, { onNavigate } = {}) {
  let currentDoc = doc;
  const canvas = h('canvas', { style: { width: '100%', height: '100%', display: 'block', cursor: 'crosshair' } });
  const body = h('div', { style: { position: 'absolute', inset: '0' } }, canvas);
  const wrap = h('div', { style: { position: 'relative', height: '100%', minHeight: '90px' } }, body);
  const panel = makePanel({ id: 'evalgraph', title: 'Evaluation', icon: 'graph', body: wrap });

  const ro = new ResizeObserver(() => draw());
  ro.observe(body);

  function nodes() { return currentDoc.mainline(); }

  function valOf(n) {
    if (n.evalMate != null) return n.evalMate > 0 ? 100 : 0;
    if (n.evalCp != null) return winPercent(n.evalCp);
    return null;
  }

  function draw() {
    const dpr = window.devicePixelRatio || 1;
    const w = body.clientWidth, hgt = body.clientHeight;
    if (!w || !hgt) return;
    canvas.width = w * dpr; canvas.height = hgt * dpr;
    const ctx = canvas.getContext('2d');
    ctx.scale(dpr, dpr);
    const css = getComputedStyle(document.documentElement);
    const cText = css.getPropertyValue('--text-faint').trim();
    const cAccent = css.getPropertyValue('--accent').trim();
    const cErr = css.getPropertyValue('--err').trim();
    const cWarn = css.getPropertyValue('--warn').trim();
    const cWhiteFill = css.getPropertyValue('--eval-white').trim();
    const cBlackFill = css.getPropertyValue('--eval-black').trim();

    ctx.clearRect(0, 0, w, hgt);
    const line = nodes();
    const cur = currentDoc.path().length ? currentDoc.path()[currentDoc.path().length - 1] : null;
    const N = line.length;
    // midline
    ctx.strokeStyle = cText; ctx.globalAlpha = .4; ctx.lineWidth = 1;
    ctx.beginPath(); ctx.moveTo(0, hgt / 2); ctx.lineTo(w, hgt / 2); ctx.stroke();
    ctx.globalAlpha = 1;
    if (!N) return;

    const xOf = i => (i + 1) / (N + 1) * w;
    const yOf = v => hgt - (v / 100) * hgt;

    // area: white advantage above midline
    let lastV = 50;
    ctx.beginPath();
    ctx.moveTo(0, yOf(50));
    line.forEach((n, i) => {
      const v = valOf(n);
      if (v != null) lastV = v;
      ctx.lineTo(xOf(i), yOf(lastV));
    });
    ctx.lineTo(w, yOf(lastV));
    ctx.lineTo(w, yOf(50));
    ctx.closePath();
    const grad = ctx.createLinearGradient(0, 0, 0, hgt);
    grad.addColorStop(0, cWhiteFill); grad.addColorStop(1, cBlackFill);
    ctx.save();
    ctx.clip();
    ctx.fillStyle = cWhiteFill; ctx.globalAlpha = .9;
    ctx.fillRect(0, 0, w, hgt / 2);
    ctx.fillStyle = cBlackFill;
    ctx.fillRect(0, hgt / 2, w, hgt / 2);
    ctx.restore();
    // stroke
    ctx.strokeStyle = cAccent; ctx.lineWidth = 1.5;
    lastV = 50;
    ctx.beginPath(); ctx.moveTo(0, yOf(50));
    line.forEach((n, i) => {
      const v = valOf(n);
      if (v != null) lastV = v;
      ctx.lineTo(xOf(i), yOf(lastV));
    });
    ctx.stroke();
    // quality dots
    line.forEach((n, i) => {
      if (n.quality === 'blunder' || n.quality === 'mistake') {
        ctx.fillStyle = n.quality === 'blunder' ? cErr : cWarn;
        ctx.beginPath();
        ctx.arc(xOf(i), yOf(valOf(n) ?? 50), 3, 0, Math.PI * 2);
        ctx.fill();
      }
    });
    // current marker
    if (cur) {
      const i = line.indexOf(cur);
      if (i >= 0) {
        ctx.strokeStyle = cAccent; ctx.globalAlpha = .9; ctx.lineWidth = 1;
        ctx.beginPath(); ctx.moveTo(xOf(i), 0); ctx.lineTo(xOf(i), hgt); ctx.stroke();
        ctx.globalAlpha = 1;
      }
    }
  }

  canvas.addEventListener('click', e => {
    const line = nodes();
    if (!line.length) return;
    const rect = canvas.getBoundingClientRect();
    const i = Math.round((e.clientX - rect.left) / rect.width * (line.length + 1) - 1);
    const n = line[Math.max(0, Math.min(line.length - 1, i))];
    if (n) { currentDoc.goTo(n); onNavigate && onNavigate(); }
  });

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

  return { panel, draw, bind };
}
