import assert from 'node:assert/strict';
import path from 'node:path';
import test from 'node:test';

import {
  RETAIL_BUNDLE_MAPS_DIAGNOSTIC_SCHEMA,
  assertRetailBundleMapDiagnosticEvidence,
  parseRetailBundleMapDiagnosticMaps,
  retailBundleMapRunOutputPaths,
  selectRetailBundleMapRun,
} from '../tools/retail-bundle-map-diagnostics.mjs';
import { verifyRetailBundleMapsEvidence } from '../tools/verify-retail-bundle-maps-evidence.mjs';

const menuMaps = [
  { id: 'mp/one', label: 'One', modes: ['DM'] },
  { id: 'mp/two', label: 'Two', modes: ['Team DM', 'CTF'] },
  { id: 'mp/three', label: 'Three', modes: ['DM', 'Tourney'] },
];
const catalog = {
  revision: 'q4bundles-test',
  bundles: [],
  maps: menuMaps.map(({ id }) => ({ id, bundleIds: [] })),
};

test('diagnostic map CSV is explicit and rejects ambiguous selections', () => {
  assert.equal(parseRetailBundleMapDiagnosticMaps(undefined), null);
  assert.deepEqual(
    parseRetailBundleMapDiagnosticMaps(' mp/three,mp/one '),
    ['mp/three', 'mp/one']
  );
  assert.throws(() => parseRetailBundleMapDiagnosticMaps(''), /at least one map/);
  assert.throws(() => parseRetailBundleMapDiagnosticMaps('mp/one,'), /empty entries/);
  assert.throws(
    () => parseRetailBundleMapDiagnosticMaps('mp/one,mp/one'),
    /duplicate maps/
  );
});

test('diagnostic run preserves menu order and every mode for selected maps', () => {
  const canonical = selectRetailBundleMapRun(catalog, menuMaps, null);
  assert.equal(canonical.diagnostic, false);
  assert.equal(canonical.catalog, catalog);
  assert.equal(canonical.menuMaps, menuMaps);
  assert.deepEqual(canonical.selectedMapIds, menuMaps.map(({ id }) => id));

  const run = selectRetailBundleMapRun(catalog, menuMaps, ['mp/three', 'mp/one']);
  assert.equal(run.diagnostic, true);
  assert.deepEqual(run.selectedMapIds, ['mp/one', 'mp/three']);
  assert.deepEqual(run.menuMaps, [menuMaps[0], menuMaps[2]]);
  assert.deepEqual(run.catalog.maps, [catalog.maps[0], catalog.maps[2]]);
  assert.throws(
    () => selectRetailBundleMapRun(catalog, menuMaps, ['mp/missing']),
    /absent from the multiplayer menu/
  );
});

test('diagnostic outputs cannot overlap publication evidence or screenshots', () => {
  const root = path.resolve('/tmp/q4-diagnostic-output-contract');
  const canonical = retailBundleMapRunOutputPaths(null, { root });
  const diagnostic = retailBundleMapRunOutputPaths(['mp/three', 'mp/one'], { root });
  const diagnosticsRoot = path.join(root, '.selfcheck', 'diagnostics');

  assert.equal(canonical.evidencePath,
    path.join(root, '.selfcheck', 'retail-bundle-maps-e2e.json'));
  assert.equal(canonical.outDir, path.join(root, '.selfcheck', 'shots', 'retail-bundle-maps'));
  assert.notEqual(diagnostic.evidencePath, canonical.evidencePath);
  assert.notEqual(diagnostic.outDir, canonical.outDir);
  assert.equal(path.relative(diagnosticsRoot, diagnostic.evidencePath).startsWith('..'), false);
  assert.equal(path.relative(diagnosticsRoot, diagnostic.outDir).startsWith('..'), false);
});

test('diagnostic evidence is marked, subset-checked, and rejected by publication verifier', () => {
  const selected = [menuMaps[0], menuMaps[2]];
  const evidence = {
    schema: RETAIL_BUNDLE_MAPS_DIAGNOSTIC_SCHEMA,
    diagnostic: true,
    selectedMaps: selected.map(({ id, modes }) => ({ id, modes })),
    scenarios: [
      { scenario: 'mp/one::DM' },
      { scenario: 'mp/three::DM' },
      { scenario: 'mp/three::Tourney' },
    ],
    returned: { scenario: 'mp/one::DM' },
  };
  assert.equal(assertRetailBundleMapDiagnosticEvidence(evidence, selected), evidence);
  assert.throws(
    () => verifyRetailBundleMapsEvidence(evidence, {}),
    /must use schema 2/
  );
});
