import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { mix, playToWin, deviates, decide, DEFAULT_BAND } from './strategy';

// Conformance gate for the ported robot strategy: the client must fold the game seed and
// choose its action exactly as backend/internal/robot/strategy.go does, so a local vs_ai
// game plays the same. Golden fixtures come from the in-package emitter
// (backend/internal/robot/strategyfixture_test.go).

interface MixCase {
  seed: string;
  salt: string;
  nums: number[];
  value: string;
}
interface DecisionCase {
  seed: string;
  moveCount: number;
  myScore: number;
  oppScore: number;
  candScores: number[];
  rackLen: number;
  bagLen: number;
  playToWin: boolean;
  win: boolean;
  kind: string;
  index: number;
}
interface Fixture {
  playToWinPercent: number;
  mix: MixCase[];
  decisions: DecisionCase[];
}

const fx = JSON.parse(
  readFileSync(new URL('./testdata/strategy.json', import.meta.url), 'utf8'),
) as Fixture;

describe('robot strategy parity vs Go', () => {
  it('mix folds the seed like Go FNV-1a', () => {
    for (const c of fx.mix) {
      expect(mix(BigInt(c.seed), c.salt, ...c.nums), `${c.seed}/${c.salt}/${c.nums}`).toBe(BigInt(c.value));
    }
  });

  it('chooses the same action as the Go robot', () => {
    for (const c of fx.decisions) {
      const seed = BigInt(c.seed);
      const cands = c.candScores.map((score, index) => ({ score, index }));
      const rack = new Array<string>(c.rackLen).fill('a');

      expect(playToWin(seed), `playToWin ${c.seed}`).toBe(c.playToWin);

      // the per-turn intent after the occasional off-strategy deviation
      let win = playToWin(seed);
      if (deviates(seed, c.moveCount, c.bagLen)) win = !win;
      expect(win, `win ${c.seed}/${c.moveCount}/${c.bagLen}`).toBe(c.win);

      const dec = decide(seed, c.moveCount, cands, c.myScore, c.oppScore, rack, c.bagLen, DEFAULT_BAND);
      expect(dec.kind, `kind ${JSON.stringify(c)}`).toBe(c.kind);
      if (dec.kind === 'play') {
        expect(dec.move.index, `index ${JSON.stringify(c)}`).toBe(c.index);
      } else if (dec.kind === 'exchange') {
        expect(dec.exchange.length).toBe(c.rackLen);
      }
    }
  });
});
