'use strict';

const { SourceIngestContext, collectIngestRecords } = require('../sources/base');
const { PRIVACY_CLASS_ORDER, RECORD_TYPES, privacyClassAllows } = require('../sources/record-types');
const sourceRegistry = require('../sources/registry');
const { ensureBuiltinSourceAdapters } = require('../sources/builtin');
const { indexMemory } = require('./source-indexer');

function createSessionIngestService({ brain = null, registry = sourceRegistry } = {}) {
  return new SessionIngestService({ brain, registry });
}

class SessionIngestService {
  constructor({ brain = null, registry = sourceRegistry } = {}) {
    this.brain = brain || require('../brain');
    this.registry = registry;
    ensureBuiltinSourceAdapters();
  }

  async ingestSource(sourceRef, { privacyFloor = null, dryRun = false } = {}) {
    const adapter = this.registry.instantiate(sourceRef.adapterId);
    if (!adapter) throw new Error(`Unknown source adapter: ${sourceRef.adapterId}`);

    const context = new SourceIngestContext();
    const { records, diagnostics, source } = await collectIngestRecords(adapter, sourceRef, { context });
    const result = {
      adapterId: adapter.schema.adapterId,
      sourceId: source.sourceId,
      records: records.length,
      memoryRecords: 0,
      inserted: 0,
      indexed: 0,
      skipped: 0,
      rejected: 0,
      diagnostics,
      dryRun,
    };

    for (const record of records) {
      if (record.type !== RECORD_TYPES.MEMORY_RECORD) continue;
      result.memoryRecords += 1;
      if (!privacyClassAllows({ recordClass: record.privacyClass, floor: privacyFloor })) {
        result.rejected += 1;
        result.diagnostics.push({
          level: 'warn',
          message: `Rejected ${record.itemId} above privacy floor ${privacyFloor}`,
          sourceId: record.sourceId,
          itemId: record.itemId,
        });
        continue;
      }
      if (dryRun) continue;
      const memory = toBrainMemory(record, adapter.schema.adapterId);
      const downgrade = this._privacyDowngrade(memory, record);
      if (downgrade) {
        result.rejected += 1;
        result.diagnostics.push({
          level: 'warn',
          message: downgrade,
          sourceId: record.sourceId,
          itemId: record.itemId,
        });
        continue;
      }
      const inserted = this.brain.insertMemory(memory);
      if (inserted) {
        result.inserted += 1;
        try {
          indexMemory({ ...memory, id: inserted.id }, { brain: this.brain });
          result.indexed += 1;
        } catch (err) {
          result.diagnostics.push({
            level: 'warn',
            message: `Indexing failed for ${record.itemId}: ${err.message}`,
            sourceId: record.sourceId,
            itemId: record.itemId,
          });
        }
      } else {
        result.skipped += 1;
      }
    }

    this._auditSourceIngest(result);
    return result;
  }

  _privacyDowngrade(memory, record) {
    if (!this.brain || typeof this.brain.getDb !== 'function') return null;
    try {
      const existing = this.brain.getDb().prepare(
        'SELECT metadata FROM memories WHERE source = ? AND source_id = ?'
      ).get(memory.source, memory.source_id);
      if (!existing) return null;
      const metadata = JSON.parse(existing.metadata || '{}');
      const previous = metadata.privacyClass || '';
      const next = record.privacyClass || '';
      if (!previous || !next) return null;
      const previousRank = PRIVACY_CLASS_ORDER.indexOf(previous);
      const nextRank = PRIVACY_CLASS_ORDER.indexOf(next);
      if (previousRank >= 0 && nextRank >= 0 && nextRank < previousRank) {
        return `Rejected privacy downgrade for ${memory.source_id}: ${previous} -> ${next}`;
      }
    } catch {}
    return null;
  }

  _auditSourceIngest(result) {
    if (!this.brain || typeof this.brain.logWrite !== 'function' || result.dryRun) return;
    try {
      this.brain.logWrite('source_ingest', 'memories', {
        id: result.sourceId,
        source: result.adapterId,
        subject: `inserted:${result.inserted};indexed:${result.indexed};rejected:${result.rejected}`,
      });
    } catch {}
  }
}

function toBrainMemory(record, adapterId) {
  const metadata = {
    ...(record.metadata || {}),
    adapterId,
    sourceId: record.sourceId,
    sourceFile: record.sourceFile,
    itemId: record.itemId,
    role: record.role || '',
    transforms: record.transforms || [],
    ingestMode: record.ingestMode || '',
    privacyClass: record.privacyClass || '',
  };
  return {
    source: adapterId,
    source_id: `${record.sourceId}:${record.itemId}`,
    source_channel: metadata.cwd || metadata.sessionId || '',
    memory_type: record.memoryType,
    direction: directionForRole(record.role),
    participants: metadata.participants || '',
    subject: metadata.gitBranch || metadata.cwd || record.sourceId,
    content: record.content,
    content_raw: record.contentRaw || record.content,
    metadata: JSON.stringify(metadata),
    importance: 0.55,
    timestamp: record.timestamp,
  };
}

function directionForRole(role) {
  if (role === 'user') return 'outbound';
  if (role === 'assistant' || role === 'tool') return 'observed';
  if (role === 'exchange') return 'exchange';
  return 'observed';
}

module.exports = {
  SessionIngestService,
  createSessionIngestService,
  toBrainMemory,
};
