const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { MongoClient } = require('mongodb');
const mongoReadAdapter = require('./mongoLedgerReadAdapter');
const persistentCollectionMap = mongoReadAdapter.COLLECTIONS;

const dataDir = path.join(__dirname, '..', 'data');
const statusPath = path.join(dataDir, 'mongo-live-backfill-latest.json');

function sha256(value) {
  return crypto.createHash('sha256').update(String(value)).digest('hex');
}

function stableStringify(value) {
  if (value === null || typeof value !== 'object') return JSON.stringify(value);
  if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
  return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
}

function pickNaturalId(row) {
  if (!row || typeof row !== 'object') return '';
  return row.id || row.txHash || row.proofId || row.blockHash || row.address || row.walletId || row.nodeId || row.backupId || row.chunkId || row.eventId || row.refId || '';
}

function getDbUri() {
  return process.env.AISFOR_CHAIN_LEDGER_DB_URI || process.env.DATABASE_URL || process.env.MONGODB_URI || '';
}

function isWriteRequested() {
  return process.env.AISFOR_CHAIN_LEDGER_MONGO_LIVE_WRITE === '1' || mongoReadAdapter.isReadEnabled();
}

function isWriteEnabled() {
  return isWriteRequested() && process.env.AISFOR_CHAIN_LEDGER_DB_WRITE === '1';
}

function buildDocument(item, row, index, createdAt) {
  const naturalId = pickNaturalId(row);
  const sourceHash = sha256(stableStringify(row));
  const migrationId = sha256(`${item.key}:${naturalId || index}:${sourceHash}`);
  return {
    _id: migrationId,
    _migrationId: migrationId,
    _targetCollection: item.collection,
    _sourceKey: item.key,
    _sourceIndex: index,
    _sourceNaturalId: naturalId,
    _sourceHash: sourceHash,
    _ledgerLiveKind: mongoReadAdapter.LIVE_KIND,
    _ledgerLiveTargetCollection: item.collection,
    _ledgerLiveCreatedAt: createdAt,
    migratedAt: createdAt,
    payload: row
  };
}

function buildCollectionDocuments(state, createdAt = new Date().toISOString()) {
  return persistentCollectionMap.map((item) => {
    const rows = Array.isArray(state[item.key]) ? state[item.key] : [];
    return { ...item, docs: rows.map((row, index) => buildDocument(item, row, index, createdAt)) };
  });
}

function writeJson(filePath, value) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`);
}

function readJson(filePath, fallback) {
  try {
    const stat = fs.statSync(filePath);
    return { ...JSON.parse(fs.readFileSync(filePath, 'utf8')), exists: true, fileUpdatedAt: stat.mtime.toISOString() };
  } catch (err) {
    return { ...fallback, exists: false, fileUpdatedAt: null, path: filePath };
  }
}

async function syncState(state, options = {}) {
  const uri = getDbUri();
  if (!uri) throw new Error('AISFOR_CHAIN_LEDGER_DB_URI is required for Mongo live writer sync.');

  const createdAt = new Date().toISOString();
  const collections = buildCollectionDocuments(state, createdAt);
  const client = new MongoClient(uri, { serverSelectionTimeoutMS: 5000 });

  await client.connect();
  try {
    const db = client.db();
    const rows = [];
    for (const item of collections) {
      const col = db.collection(item.collection);
      await col.deleteMany({ _ledgerLiveKind: mongoReadAdapter.LIVE_KIND });
      if (item.docs.length) {
        await col.bulkWrite(
          item.docs.map((doc) => ({
            updateOne: {
              filter: { _id: doc._id },
              update: { $set: doc },
              upsert: true
            }
          })),
          { ordered: false }
        );
      }
      await col.createIndex({ _ledgerLiveKind: 1, _sourceIndex: 1 });
      await col.createIndex({ _sourceHash: 1 });
      await col.createIndex({ _migrationId: 1 }, { unique: true });
      rows.push({ key: item.key, collection: item.collection, documents: item.docs.length });
    }

    const run = {
      ok: true,
      mode: 'mongo-live-sync',
      writesLiveCollections: true,
      liveKind: mongoReadAdapter.LIVE_KIND,
      reason: options.reason || 'manual-live-sync',
      totalDocuments: rows.reduce((sum, row) => sum + row.documents, 0),
      collections: rows,
      createdAt,
      path: statusPath
    };

    await db.collection('ledger_live_meta').updateOne(
      { _id: 'latest-ledger-json-live-backfill' },
      { $set: run, $push: { history: { createdAt, totalDocuments: run.totalDocuments, reason: run.reason } } },
      { upsert: true }
    );
    writeJson(statusPath, run);
    return run;
  } finally {
    await client.close();
  }
}

function writeLiveCollections(state, options = {}) {
  if (!isWriteEnabled()) return Promise.resolve(getStatus());
  return syncState(state, options);
}

function getStatus() {
  const latest = readJson(statusPath, {
    ok: false,
    mode: 'not-run',
    writesLiveCollections: false,
    totalDocuments: 0,
    collections: [],
    path: statusPath
  });
  return {
    ...latest,
    requested: isWriteRequested(),
    writeEnabled: isWriteEnabled(),
    statusPath
  };
}

module.exports = {
  buildCollectionDocuments,
  getStatus,
  isWriteRequested,
  isWriteEnabled,
  syncState,
  writeLiveCollections
};
