'use strict';

const assert = require('assert');
const napiChild = require('./napi_child');

module.exports = require('./common').runTestWithBuildType(test);

function test (buildType) {
  const binding = require(`./build/${buildType}/binding_type_check.node`);
  const testTable = {
    typeCastBoolean: {
      positiveValues: [true, false],
      negativeValues: [{}, [], 1, 1n, 'true', null, undefined]
    },
    typeCastNumber: {
      positiveValues: [1, NaN],
      negativeValues: [{}, [], true, 1n, '1', null, undefined]
    },
    typeCastBigInt: {
      positiveValues: [1n],
      negativeValues: [{}, [], true, 1, '1', null, undefined]
    },
    typeCastDate: {
      positiveValues: [new Date()],
      negativeValues: [{}, [], true, 1, 1n, '1', null, undefined]
    },
    typeCastString: {
      positiveValues: ['', '1'],
      negativeValues: [{}, [], true, 1, 1n, null, undefined]
    },
    typeCastSymbol: {
      positiveValues: [Symbol('1')],
      negativeValues: [{}, [], true, 1, 1n, '1', null, undefined]
    },
    typeCastObject: {
      positiveValues: [{}, new Date(), []],
      negativeValues: [true, 1, 1n, '1', null, undefined]
    },
    typeCastArray: {
      positiveValues: [[1]],
      negativeValues: [{}, true, 1, 1n, '1', null, undefined]
    },
    typeCastArrayBuffer: {
      positiveValues: [new ArrayBuffer(0)],
      negativeValues: [new Uint8Array(1), new SharedArrayBuffer(0), {}, [], null, undefined]
    },
    typeCastTypedArray: {
      positiveValues: [new Uint8Array(0)],
      negativeValues: [new ArrayBuffer(1), {}, [], null, undefined]
    },
    typeCastDataView: {
      positiveValues: [new DataView(new ArrayBuffer(0))],
      negativeValues: [new ArrayBuffer(1), null, undefined]
    },
    typeCastFunction: {
      positiveValues: [() => {}],
      negativeValues: [{}, null, undefined]
    },
    typeCastPromise: {
      positiveValues: [Promise.resolve()],
      // napi_is_promise distinguishes Promise and thenable.
      negativeValues: [{ then: () => {} }, null, undefined]
    },
    typeCastBuffer: {
      positiveValues: [Buffer.from('')],
      // napi_is_buffer doesn't distinguish between Buffer and TypedArrays.
      negativeValues: [new ArrayBuffer(1), null, undefined]
    },
    typeCastExternal: {
      positiveValues: [binding.external],
      negativeValues: [{}, null, undefined]
    },
    typeCastTypeArrayOfUint8: {
      // TypedArrayOf<uint8_t>::CheckCast doesn't distinguish between Uint8ClampedArray and Uint8Array.
      positiveValues: [new Uint8Array(0), new Uint8ClampedArray(0)],
      negativeValues: [new Int8Array(1), null, undefined]
    }
  };

  if ('typeCastSharedArrayBuffer' in binding) {
    testTable.typeCastSharedArrayBuffer = {
      positiveValues: [new SharedArrayBuffer(0)],
      negativeValues: [new Uint8Array(1), new ArrayBuffer(0), {}, [], null, undefined]
    };
  }

  if (process.argv[2] === 'child') {
    child(binding, testTable, process.argv[3], process.argv[4], parseInt(process.argv[5]));
    return;
  }

  for (const [methodName, { positiveValues, negativeValues }] of Object.entries(testTable)) {
    for (const idx of positiveValues.keys()) {
      const { status } = napiChild.spawnSync(process.execPath, [__filename, 'child', methodName, 'positiveValues', idx]);
      assert.strictEqual(status, 0, `${methodName} positive value ${idx} test failed`);
    }
    for (const idx of negativeValues.keys()) {
      const { status, signal, stderr } = napiChild.spawnSync(process.execPath, [__filename, 'child', methodName, 'negativeValues', idx], {
        encoding: 'utf8'
      });
      if (process.platform === 'win32') {
        assert.strictEqual(status, 128 + 6 /* SIGABRT */, `${methodName} negative value ${idx} test failed`);
      } else {
        assert.strictEqual(signal, 'SIGABRT', `${methodName} negative value ${idx} test failed`);
      }
      assert.ok(stderr.match(/FATAL ERROR: .*::CheckCast.*/));
    }
  }
}

async function child (binding, testTable, methodName, type, idx) {
  binding[methodName](testTable[methodName][type][idx]);
}
