/* svc/localai.js — built-in fallback AI (alpha-beta + quiescence + PSTs).
   Used only when the engine server is not reachable, so Play mode still
   works fully offline. ES module port of the original ai.js. */
'use strict';

import { Chess } from '../core/rules.js';

const VAL = [0, 100, 320, 330, 500, 900, 20000];

  // simple piece-square tables (white perspective, rank 0 = rank 1)
  const PST = {
    1: [0,0,0,0,0,0,0,0, 5,10,10,-20,-20,10,10,5, 5,-5,-10,0,0,-10,-5,5, 0,0,0,20,20,0,0,0,
        5,5,10,25,25,10,5,5, 10,10,20,30,30,20,10,10, 50,50,50,50,50,50,50,50, 0,0,0,0,0,0,0,0],
    2: [-50,-40,-30,-30,-30,-30,-40,-50, -40,-20,0,5,5,0,-20,-40, -30,5,10,15,15,10,5,-30, -30,0,15,20,20,15,0,-30,
        -30,5,15,20,20,15,5,-30, -30,0,10,15,15,10,0,-30, -40,-20,0,0,0,0,-20,-40, -50,-40,-30,-30,-30,-30,-40,-50],
    3: [-20,-10,-10,-10,-10,-10,-10,-20, -10,5,0,0,0,0,5,-10, -10,10,10,10,10,10,10,-10, -10,0,10,10,10,10,0,-10,
        -10,5,5,10,10,5,5,-10, -10,0,5,10,10,5,0,-10, -10,0,0,0,0,0,0,-10, -20,-10,-10,-10,-10,-10,-10,-20],
    4: [0,0,0,5,5,0,0,0, -5,0,0,0,0,0,0,-5, -5,0,0,0,0,0,0,-5, -5,0,0,0,0,0,0,-5,
        -5,0,0,0,0,0,0,-5, -5,0,0,0,0,0,0,-5, 5,10,10,10,10,10,10,5, 0,0,0,0,0,0,0,0],
    5: [-20,-10,-10,-5,-5,-10,-10,-20, -10,0,5,0,0,0,0,-10, -10,5,5,5,5,5,0,-10, 0,0,5,5,5,5,0,-5,
        -5,0,5,5,5,5,0,-5, -10,0,5,5,5,5,0,-10, -10,0,0,0,0,0,0,-10, -20,-10,-10,-5,-5,-10,-10,-20],
    6: [20,30,10,0,0,10,30,20, 20,20,0,0,0,0,20,20, -10,-20,-20,-20,-20,-20,-20,-10, -20,-30,-30,-40,-40,-30,-30,-20,
        -30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30, -30,-40,-40,-50,-50,-40,-40,-30],
  };

  function evaluate(game) {
    let score = 0;
    const b = game.board;
    for (let s = 0; s < 128; s++) {
      if (s & 0x88) continue;
      const pc = b[s];
      if (!pc) continue;
      const t = Math.abs(pc), f = s & 7, r = s >> 4;
      const idx64 = pc > 0 ? r * 8 + f : (7 - r) * 8 + f;
      score += Math.sign(pc) * (VAL[t] + PST[t][idx64]);
    }
    return score * game.turn; // side-to-move perspective
}

function orderedMoves(game, captOnly) {
    let moves = game.pseudoMoves();
    if (captOnly) moves = moves.filter(m => m.captured);
    moves.sort((a, b) => (b.captured * 10 - VAL[b.piece] / 100) - (a.captured * 10 - VAL[a.piece] / 100));
    return moves;
  }

  function quiesce(game, alpha, beta, deadline) {
    const stand = evaluate(game);
    if (stand >= beta) return beta;
    if (stand > alpha) alpha = stand;
    for (const m of orderedMoves(game, true)) {
      game.makeMove(m);
      if (game.inCheck(-game.turn)) { game.unmakeMove(); continue; }
      const score = -quiesce(game, -beta, -alpha, deadline);
      game.unmakeMove();
      if (score >= beta) return beta;
      if (score > alpha) alpha = score;
      if (Date.now() > deadline) break;
    }
    return alpha;
  }

  function search(game, depth, alpha, beta, deadline) {
    if (depth === 0) return quiesce(game, alpha, beta, deadline);
    let any = false;
    for (const m of orderedMoves(game)) {
      game.makeMove(m);
      if (game.inCheck(-game.turn)) { game.unmakeMove(); continue; }
      any = true;
      const score = -search(game, depth - 1, -beta, -alpha, deadline);
      game.unmakeMove();
      if (score >= beta) return beta;
      if (score > alpha) alpha = score;
      if (Date.now() > deadline) break;
    }
    if (!any) return game.inCheck(game.turn) ? -100000 + (10 - depth) : 0;
    return alpha;
  }

// returns {uci, scoreCp, depth} — scoreCp from side-to-move perspective
export function builtinAI(fen, maxDepth, timeMs) {
    const game = new Chess(fen);
    const deadline = Date.now() + (timeMs || 1500);
    const legal = game.legalMoves();
    if (!legal.length) return null;
    let best = legal[0], bestScore = 0, lastDepth = 1;
    for (let d = 1; d <= (maxDepth || 4); d++) {
      let localBest = null, localScore = -Infinity;
      for (const m of legal) {
        game.makeMove(m);
        const score = -search(game, d - 1, -Infinity, Infinity, deadline);
        game.unmakeMove();
        if (score > localScore) { localScore = score; localBest = m; }
        if (Date.now() > deadline) break;
      }
      if (Date.now() > deadline && d > 1) break;
      if (localBest) { best = localBest; bestScore = localScore; lastDepth = d; }
    }
    return { uci: game.uci(best), scoreCp: bestScore, depth: lastDepth };
}
