'use strict';

const {
  PRIVACY_CLASSES,
  assertAdapterSchema,
  assertIngestRecord,
  assertSourceRef,
} = require('./record-types');

class SourceAdapterBase {
  constructor(opts = {}) {
    this.opts = opts;
    this.schema = assertAdapterSchema(this.describeSchema());
  }

  describeSchema() {
    return this.constructor.schema || {
      adapterId: this.constructor.adapterId || this.constructor.name || 'source-adapter',
      version: '0.0.0',
      supportsIncremental: false,
      defaultPrivacyClass: PRIVACY_CLASSES.PII_POTENTIAL,
      declaredTransformations: [],
      fields: {},
    };
  }

  async *ingest() {
    throw new Error(`${this.schema.adapterId}.ingest() not implemented`);
  }

  async isCurrent() {
    return false;
  }

  async sourceSummary() {
    return null;
  }

  async close() {}

  validateSource(source) {
    return assertSourceRef({
      privacyClass: this.schema.defaultPrivacyClass,
      ...source,
      adapterId: source?.adapterId || this.schema.adapterId,
    });
  }

  validateRecord(record) {
    return assertIngestRecord(record, this.schema);
  }
}

class SourceIngestContext {
  constructor({ isCurrent = null, diagnostics = null } = {}) {
    this._isCurrent = isCurrent;
    this.diagnostics = diagnostics || [];
  }

  async isCurrent(item) {
    if (typeof this._isCurrent !== 'function') return false;
    return Boolean(await this._isCurrent(item));
  }

  warn(message, fields = {}) {
    this.diagnostics.push({ level: 'warn', message, ...fields });
  }

  error(message, fields = {}) {
    this.diagnostics.push({ level: 'error', message, ...fields });
  }
}

async function collectIngestRecords(adapter, source, { context = null, validate = true } = {}) {
  const ctx = context || new SourceIngestContext();
  const src = typeof adapter.validateSource === 'function' ? adapter.validateSource(source) : assertSourceRef(source);
  const records = [];
  for await (const record of adapter.ingest({ source: src, context: ctx })) {
    records.push(validate && typeof adapter.validateRecord === 'function'
      ? adapter.validateRecord(record)
      : record);
  }
  return { records, diagnostics: ctx.diagnostics, source: src };
}

module.exports = {
  SourceAdapterBase,
  SourceIngestContext,
  collectIngestRecords,
};
