const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { pathToFileURL } = require('node:url');

const packageRoot = path.resolve(__dirname, '..');
const packageJson = require(path.join(packageRoot, 'package.json'));

const nodeSpecs = [
  ['NdcDataProductPublish', 'ndcDataProductPublish', 'NDC Data Product Publish'],
  ['NdcDataProductRead', 'ndcDataProductRead', 'NDC Data Product Read'],
  ['NdcFoundryBinding', 'ndcFoundryBinding', 'NDC Foundry Binding'],
];
const credentialSpecs = [
  ['NdcDataProductWriterApi', 'ndcDataProductWriterApi', 'opaque'],
  ['NdcDataProductReaderApi', 'ndcDataProductReaderApi', 'opaque'],
  ['NdcFoundryBindingApi', 'ndcFoundryBindingApi', 'opaque'],
  ['NdcProviderRotatingAccessApi', 'ndcProviderRotatingAccessApi', 'rotating'],
];

const forbiddenNodeParameter = /(url|provider|tenant|connection|token|secret|password|credential)/i;
const forbiddenBindingKey = /(provider|tenant|connection|endpoint|url|credential|token|secret|payload)/i;
const forbiddenProductBrand = /n8n(?:-nodes-ndc)?/i;
const ndcNodeIcon = {
  light: 'file:../../icons/ndc.svg',
  dark: 'file:../../icons/ndc.dark.svg',
};

async function main() {
  assert.equal(packageJson.name, 'n8n-nodes-ndc');
  assert.equal(packageJson.dependencies, undefined, 'private NDC nodes must not add runtime supply-chain dependencies');
  for (const lifecycle of ['preinstall', 'install', 'postinstall']) {
    assert.equal(packageJson.scripts?.[lifecycle], undefined, `${lifecycle} lifecycle script is forbidden`);
  }
  assert.equal(packageJson.n8n.nodes.length, nodeSpecs.length, 'every registered custom node must be covered by policy');
  assert.deepEqual(
    [...packageJson.n8n.nodes].sort(),
    nodeSpecs
      .map(([className]) => `dist/nodes/${className}/${className}.node.js`)
      .sort(),
    'registered custom nodes and policy specs must stay in lockstep',
  );
  const nodes = new Map();

  for (const [className, internalName, displayName] of nodeSpecs) {
    const modulePath = path.join(packageRoot, 'dist', 'nodes', className, `${className}.node.js`);
    const NodeClass = require(modulePath)[className];
    const node = new NodeClass();
    nodes.set(className, node);
    assert.match(node.description.displayName, /^NDC /);
    assert.match(node.description.defaults.name, /^NDC /);
    assert.equal(node.description.displayName, displayName);
    assert.equal(node.description.defaults.name, displayName);
    assert.deepEqual(node.description.icon, ndcNodeIcon);
    assert.match(node.description.name, /^ndc[A-Z]/);
    assert.equal(node.description.name, internalName);
    assert.equal(`${packageJson.name}.${node.description.name}`, `${packageJson.name}.${internalName}`);
    assert.notEqual(
      node.description.usableAsTool,
      true,
      `${className} must not generate an additional *Tool runtime type`,
    );
    for (const property of node.description.properties) {
      assert.doesNotMatch(property.name, forbiddenNodeParameter, `${className}.${property.name} is transport-specific`);
    }
    assert.equal(node.description.credentials.length, 1);
    assert.match(node.description.credentials[0].name, /^ndc[A-Z]/);
    assertProductSurfaceHasNdcBrand(node.description, className);
  }

  const credentials = new Map();
  for (const [className, internalName, kind] of credentialSpecs) {
    const modulePath = path.join(packageRoot, 'dist', 'credentials', `${className}.credentials.js`);
    const CredentialClass = require(modulePath)[className];
    const credential = new CredentialClass();
    credentials.set(className, credential);
    assert.match(credential.displayName, /^NDC /);
    assert.equal(credential.name, internalName);
    assert.deepEqual(credential.icon, {
      light: 'file:../icons/ndc.svg',
      dark: 'file:../icons/ndc.dark.svg',
    });
    if (kind === 'opaque') {
      assert.deepEqual(credential.properties.map((property) => property.name), ['capability']);
      assert.equal(credential.properties[0].typeOptions.password, true);
      assert.equal(credential.authenticate.properties.headers.Authorization, '=Bearer {{$credentials.capability}}');
    } else {
      assert.deepEqual(
        credential.properties.map((property) => property.name),
        ['refreshToken', 'accessToken', 'accessExpiresAt'],
      );
      assert.equal(credential.properties[0].typeOptions.password, true);
      assert.equal(credential.properties[1].type, 'hidden');
      assert.equal(credential.properties[1].typeOptions.password, true);
      assert.equal(credential.properties[1].typeOptions.expirable, true);
      assert.equal(credential.authenticate.properties.headers.Authorization, '=Bearer {{$credentials.accessToken}}');
      assert.equal(credential.test.request.url, 'https://api.geliospro.com/api/v1/auth');
    }
    assertProductSurfaceHasNdcBrand({
      displayName: credential.displayName,
      documentationUrl: credential.documentationUrl,
      properties: credential.properties,
    }, className);
  }

  await assertProviderRotatingCredential(credentials.get('NdcProviderRotatingAccessApi'));

  for (const icon of ['ndc.svg', 'ndc.dark.svg']) {
    const iconPath = path.join(packageRoot, 'dist', 'icons', icon);
    assert.equal(fs.existsSync(iconPath), true, `${icon} was not copied`);
    assert.match(fs.readFileSync(iconPath, 'utf8'), /aria-label="NDC"/, `${icon} must expose the NDC brand`);
  }

  const contracts = require(path.join(packageRoot, 'dist', 'nodes', 'shared', 'contracts.js'));
  const constants = require(path.join(packageRoot, 'dist', 'nodes', 'shared', 'constants.js'));
  const http = require(path.join(packageRoot, 'dist', 'nodes', 'shared', 'http.js'));
  const externalContract = await import(pathToFileURL(
    path.resolve(packageRoot, '..', 'external-provider-contract', 'src', 'index.mjs'),
  ).href);
  const items = [
    {
      json: {
        sourceId: 'fleet.unit.42',
        semanticType: 'map.moving_object',
        observedAt: '2026-07-15T12:00:00.000Z',
        attributes: { speedKph: 12 },
        geometry: { type: 'Point', coordinates: [37.61, 55.75] },
      },
    },
  ];
  const publish = contracts.buildPublishPayload(
    items,
    'execution-42',
    'workflow-7',
    'node-3',
    'fleet.positions.current.v1',
    0,
  );
  assert.deepEqual(externalContract.validateDataProductPublish(publish), { ok: true, errors: [] });
  assert.deepEqual(
    publish,
    contracts.buildPublishPayload(items, 'execution-42', 'workflow-7', 'node-3', 'fleet.positions.current.v1', 0),
    'same execution chunk must produce the same idempotency envelope',
  );
  assert.notEqual(
    publish.batch.idempotencyKey,
    contracts.buildPublishPayload(items, 'execution-42', 'workflow-7', 'node-3', 'fleet.positions.current.v2', 0).batch.idempotencyKey,
  );
  const zoneItems = [{
    json: {
      sourceId: 'gelios-zone-42',
      semanticType: 'map.zone',
      observedAt: '2026-07-15T12:10:00.000Z',
      attributes: { display_name: 'Zone 42' },
      geometry: {
        type: 'Polygon',
        coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.60, 55.74]]],
      },
    },
  }];
  const replacement = contracts.buildPublishPayload(
    zoneItems,
    'execution-43',
    'workflow-7',
    'node-zones',
    'map.zones.current.v1',
    0,
    'replace',
  );
  assert.equal(externalContract.validateDataProductPublish(replacement).ok, true);
  assert.equal(replacement.batch.mode, 'replace');
  assert.equal(replacement.batch.generationAt, zoneItems[0].json.observedAt);
  const replacementReplay = contracts.buildPublishPayload(
    zoneItems,
    'execution-replay',
    'workflow-other',
    'node-other',
    'map.zones.current.v1',
    0,
    'replace',
  );
  assert.equal(
    replacementReplay.batch.runId,
    replacement.batch.runId,
    'the same complete generation must keep one run identity across executions',
  );
  assert.equal(
    replacementReplay.batch.idempotencyKey,
    replacement.batch.idempotencyKey,
    'the same complete generation must replay through the data plane idempotently',
  );
  const changedReplacement = contracts.buildPublishPayload(
    [{ json: { ...zoneItems[0].json, attributes: { display_name: 'Zone 42 changed' } } }],
    'execution-replay',
    'workflow-other',
    'node-other',
    'map.zones.current.v1',
    0,
    'replace',
  );
  assert.notEqual(
    changedReplacement.batch.idempotencyKey,
    replacement.batch.idempotencyKey,
    'different content at one declared generation must not alias an accepted replay',
  );
  const replacementBatch = contracts.buildPublishPayload(
    [{ json: { generationAt: zoneItems[0].json.observedAt, facts: zoneItems.map((item) => item.json) } }],
    'execution-44',
    'workflow-7',
    'node-zones',
    'map.zones.current.v1',
    0,
    'replace',
  );
  assert.deepEqual(replacementBatch.facts, replacement.facts);
  assert.equal(replacementBatch.batch.generationAt, zoneItems[0].json.observedAt);
  assert.equal(replacementBatch.batch.idempotencyKey, replacement.batch.idempotencyKey);
  const emptyReplacement = contracts.buildPublishPayload(
    [{ json: { generationAt: '2026-07-15T13:00:00.000Z', facts: [] } }],
    'execution-45',
    'workflow-7',
    'node-zones',
    'map.zones.current.v1',
    0,
    'replace',
  );
  assert.deepEqual(emptyReplacement.facts, []);
  assert.equal(externalContract.validateDataProductPublish(emptyReplacement).ok, true);
  assert.throws(
    () => contracts.buildPublishPayload(
      [{ json: { generationAt: '2026-07-15T13:00:00.000Z', facts: zoneItems.map((item) => item.json) } }],
      'execution-46',
      'workflow-7',
      'node-zones',
      'map.zones.current.v1',
      0,
      'replace',
    ),
    /replace_generationAt_observedAt_mismatch/,
  );
  assert.throws(
    () => contracts.buildPublishPayload(
      [{ json: { ...items[0].json, attributes: { accessToken: 'forbidden' } } }],
      'execution-42',
      'workflow-7',
      'node-3',
      'fleet.positions.current.v1',
      0,
    ),
    /secret_material_forbidden/,
  );

  const foundry = contracts.buildFoundryBindingPayload({
    applicationId: '11111111-1111-4111-8111-111111111111',
    pageId: 'map',
    binding: {
      id: 'fleet-live-points',
      dataProductId: 'fleet.positions.current.v1',
      slotId: 'points',
      semanticTypes: ['map.moving_object'],
      fieldProjection: ['coordinates.longitude', 'coordinates.latitude'],
    },
  });
  assertNoForbiddenKeys(foundry, forbiddenBindingKey);
  assert.equal(externalContract.validateFoundryBindingUpsert(foundry).ok, true);
  assert.equal(foundry.schemaVersion, externalContract.FOUNDRY_BINDING_UPSERT_SCHEMA_VERSION);
  assert.equal(foundry.binding.dataProductId, 'fleet.positions.current.v1');
  assert.equal(foundry.binding.slotId, 'points');
  assert.match(foundry.idempotencyKey, /^foundry-binding-[a-f0-9]{32}$/);

  assert.equal(constants.DATA_PRODUCT_WRITER_CATALOG_PATH, '/internal/data-plane/v1/writer/data-products');
  assert.equal(constants.DATA_PRODUCT_READER_CATALOG_PATH, '/internal/data-plane/v1/reader/data-products');
  assert.equal(constants.DATA_PRODUCT_BASE_PATH, '/internal/data-plane/v1/data-products');
  assert.equal(constants.DATA_PLANE_DEFAULT_BASE_URL, 'http://external-data-plane:18106');
  assert.equal(constants.FOUNDRY_DEFAULT_BASE_URL, 'http://nodedc-module-foundry:3333');
  const previousDefaultProbe = process.env.NDC_DATA_PLANE_BASE_URL;
  delete process.env.NDC_DATA_PLANE_BASE_URL;
  assert.equal(
    http.serviceBaseUrl(constants.DATA_PLANE_BASE_URL_ENV, constants.DATA_PLANE_DEFAULT_BASE_URL),
    'http://external-data-plane:18106',
  );
  restoreEnvironment('NDC_DATA_PLANE_BASE_URL', previousDefaultProbe);

  const readProperties = nodes.get('NdcDataProductRead').description.properties.map((property) => property.name);
  assert.equal(readProperties.includes('mode'), true, 'read mode must expose snapshot/history explicitly');
  assert.equal(readProperties.includes('cursor'), true, 'history endpoint must expose its opaque continuation cursor');
  assert.equal(readProperties.includes('sourceIds'), true, 'history filtering remains provider-neutral');
  const pageSizeProperty = nodes.get('NdcDataProductRead').description.properties.find((property) => property.name === 'pageSize');
  assert.equal(pageSizeProperty.typeOptions.minValue, 1);
  assert.equal(pageSizeProperty.typeOptions.maxValue, 5000, 'bounded snapshot v1 must not expose more than 5000 facts');
  assert.equal(pageSizeProperty.default <= pageSizeProperty.typeOptions.maxValue, true);
  const slotProperty = nodes.get('NdcFoundryBinding').description.properties.find((property) => property.name === 'slotId');
  assert.equal(slotProperty.default, 'points');

  await assertNodeHttpContracts(nodes, externalContract, constants);

  console.log('n8n-nodes-ndc package policy: ok');
}

async function assertProviderRotatingCredential(credential) {
  let requestCount = 0;
  let releaseRequest;
  const request = new Promise((resolve) => { releaseRequest = resolve; });
  const helper = {
    helpers: {
      async httpRequest(options) {
        requestCount += 1;
        assert.equal(options.method, 'POST');
        assert.equal(options.url, 'https://api.geliospro.com/api/v1/auth/refresh');
        assert.deepEqual(options.body, { refresh_token: 'refresh-old-single-flight' });
        assert.equal(options.headers.Authorization, undefined);
        return request;
      },
    },
  };
  const credentials = { refreshToken: 'refresh-old-single-flight', accessToken: '' };
  const first = credential.preAuthentication.call(helper, credentials);
  const second = credential.preAuthentication.call(helper, credentials);
  await new Promise((resolve) => setImmediate(resolve));
  assert.equal(requestCount, 1, 'rotating refresh must be single-flight inside the one-service L2 runtime');
  releaseRequest({
    access_token: 'access-new',
    refresh_token: 'refresh-new',
    token_type: 'Bearer',
    expires_in: 3600,
  });
  const [firstTokens, secondTokens] = await Promise.all([first, second]);
  assert.deepEqual(firstTokens, secondTokens);
  assert.equal(firstTokens.accessToken, 'access-new');
  assert.equal(firstTokens.refreshToken, 'refresh-new');
  assert.match(firstTokens.accessExpiresAt, /^\d{4}-\d{2}-\d{2}T/);

  const replay = await credential.preAuthentication.call(helper, credentials);
  assert.deepEqual(replay, firstTokens);
  assert.equal(requestCount, 1, 'a stale concurrent caller must reuse the recent rotation result');

  const leakedRefresh = 'refresh-token-must-not-leak';
  await assert.rejects(
    credential.preAuthentication.call({
      helpers: {
        async httpRequest() {
          throw new Error(`remote rejected ${leakedRefresh}`);
        },
      },
    }, { refreshToken: leakedRefresh, accessToken: '' }),
    (error) => error?.message === 'provider_access_refresh_failed'
      && !error.message.includes(leakedRefresh),
  );
}

function assertProductSurfaceHasNdcBrand(surface, className) {
  const visibleStrings = [];
  collectVisibleStrings(surface, visibleStrings);
  for (const value of visibleStrings) {
    assert.doesNotMatch(value, forbiddenProductBrand, `${className} leaks a technical package/runtime brand: ${value}`);
  }
}

function collectVisibleStrings(value, output, key = '') {
  if (typeof value === 'string') {
    if (!['name', 'type', 'value'].includes(key)) output.push(value);
    return;
  }
  if (Array.isArray(value)) {
    for (const item of value) collectVisibleStrings(item, output, key);
    return;
  }
  if (!value || typeof value !== 'object') return;
  for (const [childKey, childValue] of Object.entries(value)) {
    if (['credentials', 'icon'].includes(childKey)) continue;
    collectVisibleStrings(childValue, output, childKey);
  }
}

async function assertNodeHttpContracts(nodes, externalContract, constants) {
  const previousDataPlaneUrl = process.env.NDC_DATA_PLANE_BASE_URL;
  const previousFoundryUrl = process.env.NDC_FOUNDRY_BASE_URL;
  process.env.NDC_DATA_PLANE_BASE_URL = 'http://data-plane.test/';
  process.env.NDC_FOUNDRY_BASE_URL = 'http://foundry.test/';
  try {
    const publishCalls = [];
    const publishContext = executionContext({
      parameters: { dataProductId: 'fleet.positions.current.v1', sequence: 0 },
      input: [{ json: canonicalFact() }],
      response: { ok: true, publishedFactCount: 1 },
      calls: publishCalls,
    });
    const publishResult = await nodes.get('NdcDataProductPublish').execute.call(publishContext);
    assert.equal(publishCalls[0].credentialName, 'ndcDataProductWriterApi');
    assert.equal(publishCalls[0].options.method, 'POST');
    assert.equal(
      publishCalls[0].options.url,
      'http://data-plane.test/internal/data-plane/v1/data-products/fleet.positions.current.v1/publish',
    );
    assert.equal(externalContract.validateDataProductPublish(publishCalls[0].options.body).ok, true);
    assert.equal(publishResult[0][0].json.publishedFactCount, 1);

    const replacementCalls = [];
    const replacementContext = executionContext({
      typeVersion: 2,
      parameters: { dataProductId: 'map.zones.current.v1', publishMode: 'replace', sequence: 0 },
      input: [{
        json: {
          generationAt: '2026-07-15T12:10:00.000Z',
          facts: [{
            sourceId: 'gelios-zone-42',
            semanticType: 'map.zone',
            observedAt: '2026-07-15T12:10:00.000Z',
            attributes: { display_name: 'Zone 42' },
            geometry: { type: 'Polygon', coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.60, 55.74]]] },
          }],
        },
      }],
      response: { ok: true, publishedFactCount: 1, currentRemovedCount: 0 },
      calls: replacementCalls,
    });
    await nodes.get('NdcDataProductPublish').execute.call(replacementContext);
    assert.equal(replacementCalls[0].options.body.batch.mode, 'replace');
    assert.equal(externalContract.validateDataProductPublish(replacementCalls[0].options.body).ok, true);

    const readCalls = [];
    const readContext = executionContext({
      parameters: { dataProductId: 'fleet.positions.current.v1', pageSize: 1000 },
      response: {
        schemaVersion: 'nodedc.data-product.snapshot/v1',
        dataProduct: { id: 'fleet.positions.current.v1', version: '1.0.0' },
        generatedAt: '2026-07-15T12:00:01.000Z',
        cursor: '1',
        facts: [{ ...canonicalFact(), receivedAt: '2026-07-15T12:00:00.100Z' }],
      },
      calls: readCalls,
    });
    const readResult = await nodes.get('NdcDataProductRead').execute.call(readContext);
    assert.equal(readCalls[0].credentialName, 'ndcDataProductReaderApi');
    assert.equal(readCalls[0].options.method, 'GET');
    assert.equal(
      readCalls[0].options.url,
      'http://data-plane.test/internal/data-plane/v1/data-products/fleet.positions.current.v1/snapshot',
    );
    assert.deepEqual(readCalls[0].options.qs, { limit: 1000 });
    assert.equal(readResult[0][0].json.sourceId, 'fleet.unit.42');

    const historyCalls = [];
    const historyResponse = {
      schemaVersion: 'nodedc.data-product.history/v1',
      dataProduct: { id: 'fleet.positions.current.v1', version: '1.0.0' },
      generatedAt: '2026-07-15T12:05:00.000Z',
      query: {
        from: '2026-07-15T12:00:00.000Z',
        to: '2026-07-15T12:05:00.000Z',
        resolutionMs: 60000,
        sourceIds: ['fleet.unit.42'],
        order: 'asc',
      },
      facts: [{ ...canonicalFact(), receivedAt: '2026-07-15T12:00:00.100Z', bucketStart: '2026-07-15T12:00:00.000Z' }],
      nextCursor: 'opaque_cursor',
    };
    const historyContext = executionContext({
      parameters: {
        mode: 'history',
        dataProductId: 'fleet.positions.current.v1',
        from: '2026-07-15T12:00:00.000Z',
        to: '2026-07-15T12:05:00.000Z',
        resolutionMs: 60000,
        sourceIds: 'fleet.unit.42',
        limit: 1000,
        cursor: '',
      },
      response: historyResponse,
      calls: historyCalls,
    });
    const historyResult = await nodes.get('NdcDataProductRead').execute.call(historyContext);
    assert.equal(historyCalls[0].credentialName, 'ndcDataProductReaderApi');
    assert.equal(historyCalls[0].options.method, 'GET');
    assert.equal(
      historyCalls[0].options.url,
      'http://data-plane.test/internal/data-plane/v1/data-products/fleet.positions.current.v1/history',
    );
    assert.deepEqual(historyCalls[0].options.qs, {
      from: '2026-07-15T12:00:00.000Z',
      to: '2026-07-15T12:05:00.000Z',
      resolutionMs: 60000,
      sourceIds: 'fleet.unit.42',
      limit: 1000,
    });
    assert.deepEqual(historyResult[0][0].json, historyResponse);

    const foundryCalls = [];
    const foundryContext = executionContext({
      parameters: {
        applicationId: '11111111-1111-4111-8111-111111111111',
        pageId: 'map',
        bindingId: 'fleet-live-points',
        dataProductId: 'fleet.positions.current.v1',
        slotId: 'points',
        semanticTypes: ['map.moving_object'],
        fieldProjection: ['coordinates.longitude'],
      },
      response: { ok: true, binding: { id: 'fleet-live-points' } },
      calls: foundryCalls,
    });
    await nodes.get('NdcFoundryBinding').execute.call(foundryContext);
    assert.equal(foundryCalls[0].credentialName, 'ndcFoundryBindingApi');
    assert.equal(foundryCalls[0].options.method, 'POST');
    assert.equal(foundryCalls[0].options.url, 'http://foundry.test/internal/foundry/v1/data-product-bindings');
    assertNoForbiddenKeys(foundryCalls[0].options.body, forbiddenBindingKey);

    const catalogCalls = [];
    const catalogContext = {
      helpers: {
        async httpRequestWithAuthentication(credentialName, options) {
          catalogCalls.push({ credentialName, options });
          return { dataProducts: [{ id: 'fleet.positions.current.v1', version: '1.0.0' }] };
        },
      },
    };
    const writerOptions = await nodes.get('NdcDataProductPublish').methods.loadOptions.getDataProducts.call(catalogContext);
    const readerOptions = await nodes.get('NdcDataProductRead').methods.loadOptions.getDataProducts.call(catalogContext);
    assert.deepEqual(writerOptions, [{ name: 'fleet.positions.current.v1 · 1.0.0', value: 'fleet.positions.current.v1' }]);
    assert.deepEqual(readerOptions, writerOptions);
    assert.equal(catalogCalls[0].options.url, `http://data-plane.test${constants.DATA_PRODUCT_WRITER_CATALOG_PATH}`);
    assert.equal(catalogCalls[1].options.url, `http://data-plane.test${constants.DATA_PRODUCT_READER_CATALOG_PATH}`);
  } finally {
    restoreEnvironment('NDC_DATA_PLANE_BASE_URL', previousDataPlaneUrl);
    restoreEnvironment('NDC_FOUNDRY_BASE_URL', previousFoundryUrl);
  }
}

function executionContext({ parameters, input = [{ json: {} }], response, calls, typeVersion = 1 }) {
  return {
    getNodeParameter(name, _index, defaultValue) {
      return Object.prototype.hasOwnProperty.call(parameters, name) ? parameters[name] : defaultValue;
    },
    getInputData() { return input; },
    getExecutionId() { return 'execution-42'; },
    getWorkflow() { return { id: 'workflow-7' }; },
    getNode() { return { id: 'node-3', typeVersion }; },
    helpers: {
      async httpRequestWithAuthentication(credentialName, options) {
        calls.push({ credentialName, options });
        return response;
      },
    },
  };
}

function canonicalFact() {
  return {
    sourceId: 'fleet.unit.42',
    semanticType: 'map.moving_object',
    observedAt: '2026-07-15T12:00:00.000Z',
    attributes: { speedKph: 12 },
    geometry: { type: 'Point', coordinates: [37.61, 55.75] },
  };
}

function restoreEnvironment(name, value) {
  if (value === undefined) delete process.env[name];
  else process.env[name] = value;
}

function assertNoForbiddenKeys(value, pattern, pathPrefix = 'body') {
  if (Array.isArray(value)) {
    value.forEach((entry, index) => assertNoForbiddenKeys(entry, pattern, `${pathPrefix}[${index}]`));
    return;
  }
  if (!value || typeof value !== 'object') return;
  for (const [key, child] of Object.entries(value)) {
    assert.doesNotMatch(key, pattern, `${pathPrefix}.${key} contains transport material`);
    assertNoForbiddenKeys(child, pattern, `${pathPrefix}.${key}`);
  }
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
