'use strict';

const assert = require('node:assert/strict');
const { collectIngestRecords } = require('./base');
const {
  RECORD_TYPES,
  SourceValidationError,
  validateAdapterSchema,
  validateIngestRecord,
  validateSourceRef,
} = require('./record-types');

async function assertSourceAdapterContract(adapter, { source, minRecords = 1, requireMemoryRecord = true } = {}) {
  const schemaValidation = validateAdapterSchema(adapter.describeSchema ? adapter.describeSchema() : adapter.schema);
  assert.equal(schemaValidation.ok, true, schemaValidation.errors.join('; '));

  const sourceValidation = validateSourceRef(source);
  assert.equal(sourceValidation.ok, true, sourceValidation.errors.join('; '));

  const { records } = await collectIngestRecords(adapter, sourceValidation.value, { validate: false });
  assert.ok(records.length >= minRecords, `expected at least ${minRecords} source records`);

  let memoryRecords = 0;
  for (const record of records) {
    const validation = validateIngestRecord(record, schemaValidation.value);
    assert.equal(validation.ok, true, validation.errors.join('; '));
    if (record.type === RECORD_TYPES.MEMORY_RECORD) memoryRecords += 1;
  }

  if (requireMemoryRecord) {
    assert.ok(memoryRecords > 0, 'expected at least one memory_record');
  }

  return { schema: schemaValidation.value, records };
}

async function assertSourceAdapterContractFails(adapter, { source, pattern } = {}) {
  let error = null;
  try {
    await assertSourceAdapterContract(adapter, { source });
  } catch (err) {
    error = err;
  }
  assert.ok(error, 'expected adapter contract to fail');
  if (pattern) assert.match(error.message, pattern);
  return error;
}

async function assertAdapterRejectsInvalidRecords(adapter, { source } = {}) {
  await assert.rejects(async () => {
    await collectIngestRecords(adapter, source);
  }, SourceValidationError);
}

module.exports = {
  assertAdapterRejectsInvalidRecords,
  assertSourceAdapterContract,
  assertSourceAdapterContractFails,
};
