import assert from 'node:assert/strict';
import { readdir, readFile } from 'node:fs/promises';
import test from 'node:test';

const root = new URL('../', import.meta.url);

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('replay anchor keeps legacy indices while the exact retail declaration owns the body', async () => {
  const [anchor, patch, nativeBuild, wasmBuild, sourceOffer, smoke] = await Promise.all([
    readFile(new URL('content-src/baseoq4/pak0/def/weapons/00_napalmgun_openq4.def', root), 'utf8'),
    readFile(new URL('port/native/patches/0024-replace-replay-anchor-with-retail-decl.patch', root), 'utf8'),
    readFile(new URL('tools/build-native.sh', root), 'utf8'),
    readFile(new URL('tools/build-wasm.sh', root), 'utf8'),
    readFile(new URL('tools/stage-source-offer.sh', root), 'utf8'),
    readFile(new URL('port/native/smoke-dedicated.sh', root), 'utf8'),
  ]);

  assert.deepEqual(
    [...anchor.matchAll(/^entityDef\s+(\S+)/gm)].map((match) => match[1]),
    ['damage_openq4_napalmGlobResidueBurn', 'impact_napalm_projectile'],
  );
  assert.match(anchor, /SPDX-License-Identifier: CC0-1\.0/);
  assert.doesNotMatch(anchor, /q4web_retail_override|net_syncPhysics|models\/|effects\/|fx_|fuse|gravity|speed/);

  assert.match(patch, /type != DECL_ENTITYDEF/);
  assert.match(patch, /name\.Icmp\( "impact_napalm_projectile" \)/);
  assert.match(patch, /previousFileName\.Icmp\( "def\/weapons\/00_napalmgun_openq4\.def" \)/);
  assert.match(patch, /currentFileName\.Icmp\( "def\/weapons\/napalmgun\.def" \)/);
  assert.match(patch, /idDeclLocal \*\*sourceLink = &decl->sourceFile->decls/);
  assert.match(patch, /Replay anchor '%s' is missing from its source-file list/);
  assert.match(patch, /decl->sourceFile = this/);
  assert.match(patch, /decl->needsPrecache = false/);
  assert.match(patch, /else if \( TryReplaceQ4WebReplayAnchor\( newDecl/);
  assert.match(patch, /if \( !TryReplaceQ4WebReplayAnchor\( decl/);
  assert.match(patch, /if \( referencedThisLevel \) \{[\s\S]*decl->ParseLocal\(\)/);
  assert.match(patch, /indexToStoreAt != decl->index/);
  assert.match(patch, /Packed replay anchor index mismatch: expected %d, got %d/);

  for (const script of [nativeBuild, wasmBuild, sourceOffer]) {
    assert.equal(
      patchList(script, 'OPENQ4_PATCH_FILES')
        .filter((name) => name === '0024-replace-replay-anchor-with-retail-decl.patch').length,
      1,
    );
  }

  for (const build of [nativeBuild, wasmBuild]) {
    assert.match(build, /FREE_RUNTIME_SOURCE/);
    assert.match(build, /--fingerprint-only/);
    assert.match(build, /runtimeSourceSha256/);
    assert.match(build, /rm -rf --[\s\S]*content\/baseoq4\/pak0/);
    assert.match(build, /cp -a -- "\$\{FREE_RUNTIME_SOURCE\}\/pak0"/);
  }
  assert.match(nativeBuild, /cmp -- "\$\{FREE_RUNTIME_EXPECTED\}\/\$\{pack\}"/);
  assert.match(nativeBuild, /0013-require-free-web-runtime-paks\.patch/);
  assert.match(wasmBuild, /cmp "\$\{expected\}" "\$\{sp_pack\}"/);
  assert.match(wasmBuild, /cmp "\$\{expected\}" "\$\{mp_pack\}"/);

  for (const evidence of [
    '1647: damage_openq4_napalmglobresidueburn',
    '1648: impact_napalm_projectile',
    '1687: weapon_hyperblaster',
    '1769: weapon_rocketlauncher',
    '1785: weapon_shotgun',
    '1806 of 1806 entityDef',
    'source: def/weapons/napalmgun.def:',
    '"def_emit_damage"',
    '"damage_napalmGlobEmit"',
    '"net_syncPhysics"',
  ]) {
    assert.ok(smoke.includes(evidence), `native smoke is missing ${evidence}`);
  }
  assert.match(smoke, /IMPACT_STATUS_FILE/);
});

test('published patches contain no pruned OpenQ4 runtime-file diff or removed body', async () => {
  const patchDirectory = new URL('port/native/patches/', root);
  const names = (await readdir(patchDirectory)).filter((name) => name.endsWith('.patch'));
  const contents = await Promise.all(names.map((name) => readFile(new URL(name, patchDirectory), 'utf8')));
  const combined = contents.join('\n');

  assert.doesNotMatch(combined, /^diff --git a\/content\/baseoq4\/pak[01]\//m);
  assert.doesNotMatch(combined, /flame_gun_globsmall\.ase|globburn\.fx|flyburn\.fx/);
});
