/* modes/editor.js — Board editor: free piece placement, castling rights,
   en-passant, move counters, validation with legality warnings, FEN io,
   presets, copy-as-image, transitions to Analysis / Play. */
'use strict';

import { h, clear } from '../ui/dom.js';
import { icon } from '../ui/icons.js';
import { toast, makePanel } from '../ui/widgets.js';
import { Chess, START_FEN, sq, fileOf, rankOf, onBoard } from '../core/rules.js';
import { Board } from '../board/board.js';
import { pieceSVG } from '../board/pieces.js';
import { settings } from '../svc/settings.js';
import { commands } from '../svc/commands.js';

const PRESETS = [
  { label: 'Starting position', fen: START_FEN },
  { label: 'Empty board', fen: '8/8/8/8/8/8/8/8 w - - 0 1' },
  { label: 'K+P vs K (won)', fen: '8/8/8/4k3/8/8/4P3/4K3 w - - 0 1' },
  { label: 'K+Q vs K', fen: '8/8/8/4k3/8/8/8/Q3K3 w - - 0 1' },
  { label: 'K+R vs K', fen: '8/8/8/4k3/8/8/8/R3K3 w - - 0 1' },
  { label: 'Lucena position', fen: '1K1k4/1P6/8/8/8/8/r7/2R5 w - - 0 1' },
  { label: 'Philidor position', fen: '2k5/8/8/8/4p3/8/3R4/4K2r w - - 0 1' },
  { label: 'B+N mate corner', fen: '8/8/8/8/8/2k5/8/K1N2B2 w - - 0 1' },
];

export const editorMode = {
  id: 'editor', title: 'Board editor', icon: 'pencil', kbd: 'Ctrl+8',

  mount(root, ctx) {
    this.ctx = ctx;
    this.pos = new Chess(START_FEN);
    this.selPiece = null;              // palette selection: 'P','n',… or 'erase'

    const boardBox = h('div.bs-board', { style: { flex: '1', minHeight: '0' } });
    this.board = new Board(boardBox, { interactive: false });

    // custom editor input: click board places selected piece / drag existing
    this.board.el.addEventListener('pointerdown', e => {
      if (e.button !== 0) return;
      const target = this.board._fromPoint(e.clientX, e.clientY);
      if (target < 0) return;
      if (this.selPiece === 'erase') { this.setPieceAt(target, 0); return; }
      if (this.selPiece) { this.setPieceAt(target, this.selPiece); return; }
      // drag move: pick up piece
      const cur = this.pos.board[target];
      if (!cur) return;
      this._drag = { from: target, piece: cur };
    });
    this.board.el.addEventListener('pointerup', e => {
      if (!this._drag) return;
      const d = this._drag; this._drag = null;
      const to = this.board._fromPoint(e.clientX, e.clientY);
      if (to === d.from) return;
      this.pos.board[d.from] = 0;
      if (to >= 0) this.pos.board[to] = d.piece;
      this.sync();
    });

    /* ---- palette ---- */
    const palette = h('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px', padding: '10px' } });
    const mkBtn = (letter) => {
      const btn = h('button', {
        class: 'btn', style: { width: '44px', height: '44px', padding: '2px' },
        html: pieceSVG(settings.get('pieces.set'), letter),
        title: 'Place ' + letter,
        onclick: () => { this.selPiece = this.selPiece === letter ? null : letter; paintPalette(); },
      });
      btn.dataset.piece = letter;
      return btn;
    };
    const rows = [
      ['K', 'Q', 'R', 'B', 'N', 'P'].map(mkBtn),
      ['k', 'q', 'r', 'b', 'n', 'p'].map(mkBtn),
    ];
    const eraseBtn = h('button.btn', {
      html: icon('close') + '<span>Erase</span>', style: { height: '32px' },
      onclick: () => { this.selPiece = this.selPiece === 'erase' ? null : 'erase'; paintPalette(); },
    });
    const paintPalette = () => {
      palette.querySelectorAll('button').forEach(b => b.style.outline = '');
      const sel = this.selPiece === 'erase' ? eraseBtn : palette.querySelector(`[data-piece="${this.selPiece}"]`);
      if (sel) sel.style.outline = '2px solid var(--accent)';
    };
    palette.append(
      h('div', { style: { display: 'flex', gap: '4px' } }, rows[0]),
      h('div', { style: { display: 'flex', gap: '4px' } }, rows[1]),
      eraseBtn,
      h('div.sr-hint', {}, 'Select a piece, then click squares. No selection: drag pieces on the board. Drop outside to remove.'),
    );

    /* ---- position settings ---- */
    this.stmRow = h('div.radio-row', {},
      h('button.seg', { onclick: () => { this.setStm('w'); } }, 'White to move'),
      h('button.seg', { onclick: () => { this.setStm('b'); } }, 'Black to move'));
    const cr = ['K', 'Q', 'k', 'q'].map(c => h('label.checkbox', {},
      h('input', { type: 'checkbox', dataset: { cr: c }, onchange: () => this.applyMeta() }),
      c === 'K' ? 'White O-O' : c === 'Q' ? 'White O-O-O' : c === 'k' ? 'Black O-O' : 'Black O-O-O'));
    this.crBoxes = cr;
    this.epInput = h('input.input', { placeholder: '—', style: { width: '54px' }, onchange: () => this.applyMeta() });
    this.halfInput = h('input.input', { type: 'number', value: 0, min: 0, style: { width: '64px' }, onchange: () => this.applyMeta() });
    this.fullInput = h('input.input', { type: 'number', value: 1, min: 1, style: { width: '64px' }, onchange: () => this.applyMeta() });
    this.fenInput = h('input.input.mono', { style: { width: '100%', fontSize: '11px' }, onchange: () => this.loadFen(this.fenInput.value) });
    this.warnEl = h('div', { style: { fontSize: 'var(--fs-12)', color: 'var(--warn-text)', minHeight: '18px' } });
    this.materialEl = h('div.muted', { style: { fontSize: 'var(--fs-12)' } });

    const presetSel = h('select.select', { onchange: () => { if (presetSel.value) this.loadFen(presetSel.value); presetSel.value = ''; } },
      h('option', { value: '' }, 'Load preset…'),
      ...PRESETS.map(p => h('option', { value: p.fen }, p.label)));

    const controls = h('div', { style: { display: 'flex', flexDirection: 'column', gap: '10px', padding: '10px' } },
      this.stmRow,
      h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px' } }, cr),
      h('div.form-grid', {},
        h('label', {}, 'En passant'), this.epInput,
        h('label', {}, 'Halfmove clock'), this.halfInput,
        h('label', {}, 'Move number'), this.fullInput),
      presetSel,
      h('div.field', {}, h('label', {}, 'FEN'), this.fenInput),
      this.warnEl,
      this.materialEl,
      h('div', { style: { display: 'flex', gap: '6px', flexWrap: 'wrap' } },
        h('button.btn.btn-sm', { html: icon('copy') + '<span>Copy FEN</span>', onclick: () => navigator.clipboard.writeText(this.fen()).then(() => toast('FEN copied', 'ok')) }),
        h('button.btn.btn-sm', { html: icon('camera') + '<span>Copy image</span>', onclick: () => this.copyImage() }),
        h('button.btn.btn-sm', { html: icon('refresh') + '<span>Clear</span>', onclick: () => this.loadFen('8/8/8/8/8/8/8/8 w - - 0 1') })),
      h('div', { style: { display: 'flex', gap: '6px' } },
        this.toAnalysis = h('button.btn.btn-primary', { html: icon('microscope') + '<span>Analyse</span>', onclick: () => this.transition('analysis') }),
        this.toPlay = h('button.btn', { html: icon('board') + '<span>Play vs engine</span>', onclick: () => this.transition('play') })),
    );

    const panel = makePanel({ id: 'editor-controls', title: 'Position', icon: 'pencil', body: controls, collapsible: false });
    const palettePanel = makePanel({ id: 'editor-palette', title: 'Pieces', icon: 'grid', body: palette, collapsible: false });

    root.append(h('div.dock-row', {},
      h('div.dock-center', {}, h('div', { style: { display: 'flex', height: '100%' } },
        h('div', { style: { flex: 1, minWidth: 0, padding: '10px', containerType: 'size', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, boardBox))),
      h('div.dock-side', { style: { width: '300px', borderLeft: '1px solid var(--border)' } }, palettePanel.el, panel.el)));

    commands.register({ id: 'editor.copyFen', title: 'Editor: copy FEN', category: 'Editor', icon: 'copy', when: () => ctx === this.ctx, run: () => navigator.clipboard.writeText(this.fen()) });

    this.sync();
  },

  activate() { this.sync(); },
  getDoc() { return null; },
  getSession() { return null; },

  /* ---- state ---- */
  setPieceAt(target, letter) {
    const { FEN_PIECE } = { FEN_PIECE: { p: 1, n: 2, b: 3, r: 4, q: 5, k: 6 } };
    if (letter === 0) this.pos.board[target] = 0;
    else {
      const t = FEN_PIECE[letter.toLowerCase()];
      this.pos.board[target] = letter === letter.toUpperCase() ? t : -t;
    }
    this.sync();
  },

  setStm(c) {
    this.pos.turn = c === 'w' ? 1 : -1;
    this.sync();
  },

  applyMeta() {
    let cr = 0;
    const flags = { K: 1, Q: 2, k: 4, q: 8 };
    for (const box of this.crBoxes) {
      const input = box.querySelector('input');
      if (input.checked) cr |= flags[input.dataset.cr];
    }
    this.pos.castling = cr;
    const ep = this.epInput.value.trim();
    this.pos.ep = /^[a-h][36]$/.test(ep) ? (ep.charCodeAt(1) - 49) * 16 + (ep.charCodeAt(0) - 97) : -1;
    this.pos.halfmove = Math.max(0, +this.halfInput.value || 0);
    this.pos.fullmove = Math.max(1, +this.fullInput.value || 1);
    this.sync(false);
  },

  fen() { return this.pos.fen(); },

  loadFen(fen) {
    try {
      this.pos = new Chess(fen.trim());
      this.sync();
      toast('Position loaded', 'ok');
    } catch (e) { toast('Invalid FEN', 'err'); }
  },

  validate() {
    const issues = [];
    let wk = 0, bk = 0, wp = 0, bp = 0, wTot = 0, bTot = 0;
    for (let s = 0; s < 128; s++) {
      if (!onBoard(s)) continue;
      const pc = this.pos.board[s];
      if (!pc) continue;
      const t = Math.abs(pc);
      if (pc > 0) wTot++; else bTot++;
      if (t === 6) { pc > 0 ? wk++ : bk++; }
      if (t === 1) {
        if (rankOf(s) === 0 || rankOf(s) === 7) issues.push('Pawn on back rank');
        pc > 0 ? wp++ : bp++;
      }
    }
    if (wk !== 1) issues.push(wk ? 'Multiple white kings' : 'White king missing');
    if (bk !== 1) issues.push(bk ? 'Multiple black kings' : 'Black king missing');
    if (wp > 8) issues.push('More than 8 white pawns');
    if (bp > 8) issues.push('More than 8 black pawns');
    if (wTot > 16) issues.push('More than 16 white pieces');
    if (bTot > 16) issues.push('More than 16 black pieces');
    if (wk === 1 && bk === 1) {
      const opp = -this.pos.turn;
      if (this.pos.attacked(this.pos.kingSquare(opp), this.pos.turn))
        issues.push('Side not to move is in check');
    }
    // auto-fix impossible castling rights
    const b = this.pos.board;
    const fix = (flag, kingSq, rookSq, rook, king) => {
      if ((this.pos.castling & flag) && !(b[kingSq] === king && b[rookSq] === rook)) this.pos.castling &= ~flag;
    };
    fix(1, sq(4, 0), sq(7, 0), 4, 6); fix(2, sq(4, 0), sq(0, 0), 4, 6);
    fix(4, sq(4, 7), sq(7, 7), -4, -6); fix(8, sq(4, 7), sq(0, 7), -4, -6);
    return issues;
  },

  sync(paintMeta = true) {
    const issues = this.validate();
    this.board.setPosition(this.pos, { animate: false });
    if (paintMeta) {
      this.stmRow.querySelectorAll('.seg').forEach((el, i) =>
        el.classList.toggle('active', (i === 0) === (this.pos.turn === 1)));
      const flags = { K: 1, Q: 2, k: 4, q: 8 };
      for (const box of this.crBoxes) {
        const input = box.querySelector('input');
        input.checked = !!(this.pos.castling & flags[input.dataset.cr]);
      }
      this.epInput.value = this.pos.ep >= 0 ? 'abcdefgh'[fileOf(this.pos.ep)] + (rankOf(this.pos.ep) + 1) : '';
      this.halfInput.value = this.pos.halfmove;
      this.fullInput.value = this.pos.fullmove;
    }
    this.fenInput.value = this.pos.fen();
    this.warnEl.textContent = issues.join(' · ');
    // material balance
    const VAL = { 1: 1, 2: 3, 3: 3, 4: 5, 5: 9, 6: 0 };
    let bal = 0;
    for (let s = 0; s < 128; s++) if (onBoard(s) && this.pos.board[s]) bal += Math.sign(this.pos.board[s]) * VAL[Math.abs(this.pos.board[s])];
    this.materialEl.textContent = 'Material: ' + (bal === 0 ? 'balanced' : (bal > 0 ? `White +${bal}` : `Black +${-bal}`));
    const legal = issues.length === 0;
    this.toAnalysis.disabled = !legal;
    this.toPlay.disabled = !legal;
    this.toAnalysis.title = legal ? '' : 'Fix position issues first';
  },

  async transition(target) {
    if (this.validate().length) return;
    const fen = this.fen();
    if (target === 'analysis') {
      const [{ GameDoc }, { analysisMode }] = await Promise.all([import('../core/doc.js'), import('./analysis.js')]);
      this.ctx.switchMode('analysis');
      analysisMode.openDoc(new GameDoc({ fen }), 'Edited position');
    } else {
      this.ctx.openInPlay(fen);
    }
  },

  async copyImage() {
    try {
      const size = 512, cell = size / 8;
      const canvas = h('canvas', { width: size, height: size });
      const cx = canvas.getContext('2d');
      const css = getComputedStyle(document.documentElement);
      const light = css.getPropertyValue('--sq-light').trim() || '#e3d3b5';
      const dark = css.getPropertyValue('--sq-dark').trim() || '#a8845f';
      for (let y = 0; y < 8; y++) for (let x = 0; x < 8; x++) {
        cx.fillStyle = (x + y) % 2 === 0 ? light : dark;
        cx.fillRect(x * cell, y * cell, cell, cell);
      }
      const jobs = [];
      for (const p of this.pos.pieces()) {
        const x = p.file, y = 7 - p.rank;
        const svg = pieceSVG('classic', p.piece)
          .replace(/url\(#pgw\)/g, '#f0f2f6').replace(/url\(#pgb\)/g, '#2b3040');
        const img = new Image();
        const url = URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
        jobs.push(new Promise(res => {
          img.onload = () => { cx.drawImage(img, x * cell, y * cell, cell, cell); URL.revokeObjectURL(url); res(); };
          img.onerror = res;
          img.src = url;
        }));
      }
      await Promise.all(jobs);
      const blob = await new Promise(r => canvas.toBlob(r, 'image/png'));
      if (navigator.clipboard && window.ClipboardItem) {
        await navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]);
        toast('Board image copied to clipboard', 'ok');
      } else {
        const a = h('a', { href: URL.createObjectURL(blob), download: 'position.png' });
        a.click();
        toast('Board image downloaded', 'ok');
      }
    } catch (e) { toast('Image export failed: ' + e.message, 'err'); }
  },
};
