import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import test from 'node:test';

const root = new URL('../', import.meta.url);
const patchName = '0022-skip-server-bot-ai-on-web-clients.patch';

function patchList(source, variable) {
  const body = source.match(new RegExp(`${variable}=\\(([\\s\\S]*?)\\n\\)`));
  assert.ok(body, `missing ${variable}`);
  return [...body[1].matchAll(/(\d{4}-[^/"']+\.patch)/g)].map((match) => match[1]);
}

test('WebAssembly clients skip only authoritative bot AI initialization', async () => {
  const patch = await readFile(new URL(`port/native/patches/${patchName}`, root), 'utf8');
  const guarded = patch.match(/\t\} else \{\n\+#if !defined\( __EMSCRIPTEN__ \)[\s\S]*?\n\+#endif\n \t\}/)?.[0] ?? '';

  assert.match(patch, /botItemTable = FindEntityDef\( "bot_itemtable", false \)/);
  assert.match(patch, /bot_itemtable decl not found\. Bot support disabled/);
  assert.match(guarded, /botCharacterStatsManager\.Init\(\)/);
  assert.match(guarded, /botFuzzyWeightManager\.Init\(\)/);
  assert.match(guarded, /botWeaponInfoManager\.Init\(\)/);
  assert.match(guarded, /botGoalManager\.BotSetupGoalAI\(\)/);
  assert.doesNotMatch(guarded, /if \( isServer \)/);
  assert.equal((patch.match(/__EMSCRIPTEN__/g) || []).length, 1);
});

test('every release path applies the browser bot AI guard as a GameLib patch', async () => {
  const scripts = await Promise.all([
    readFile(new URL('tools/build-wasm.sh', root), 'utf8'),
    readFile(new URL('tools/build-native.sh', root), 'utf8'),
    readFile(new URL('tools/stage-source-offer.sh', root), 'utf8')
  ]);

  for (const script of scripts) {
    const gameLib = patchList(script, 'GAMELIBS_PATCH_FILES');
    assert.equal(gameLib.at(-1), patchName);
    assert.ok(!patchList(script, 'OPENQ4_PATCH_FILES').includes(patchName));
  }
});

test('the CC0 runtime publishes only the self-authored bot configuration set', async () => {
  const source = path.resolve(new URL('../content-src/baseoq4', import.meta.url).pathname);
  const output = path.resolve(new URL('../build/wasm-free-runtime', import.meta.url).pathname);
  const sourceFiles = execFileSync('find', [source, '-type', 'f', '-printf', '%P\\n'], { encoding: 'utf8' });
  const expected = [
    'pak0/botfiles/bots/default_c.c',
    'pak0/botfiles/bots/major_c.c',
    'pak0/botfiles/bots/major_i.c',
    'pak0/botfiles/bots/major_w.c',
    'pak0/botfiles/items.c',
    'pak0/botfiles/weapons.c',
  ];
  const sourceBotFiles = sourceFiles.split('\n').filter((name) => name.includes('/botfiles/')).sort();
  assert.deepEqual(sourceBotFiles, expected);

  const pak0Members = execFileSync('unzip', ['-Z1', path.join(output, 'pak0.pk4')], { encoding: 'utf8' });
  const packedBotFiles = pak0Members.split('\n').filter((name) => name.startsWith('botfiles/')).sort();
  assert.deepEqual(packedBotFiles, expected.map((name) => name.slice('pak0/'.length)));

  const pak1Members = execFileSync('unzip', ['-Z1', path.join(output, 'pak1.pk4')], { encoding: 'utf8' });
  assert.doesNotMatch(pak1Members, /(^|\/)botfiles\//i);
});
