const fs = require('fs');
const path = require('path');
const persistentStore = require('./persistentStore');
const ledgerConsistencyService = require('./ledgerConsistencyService');
const readSwitchApprovalService = require('./ledgerReadSwitchApprovalService');

function maskValue(value) {
  if (!value) return '';
  const text = String(value);
  if (text.length <= 12) return 'configured';
  return `${text.slice(0, 8)}...${text.slice(-4)}`;
}

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

function getDryRunInfo(dataDir) {
  const dryRunPath = path.join(dataDir, 'migration-dry-run.json');
  try {
    const stat = fs.statSync(dryRunPath);
    const body = JSON.parse(fs.readFileSync(dryRunPath, 'utf8'));
    return {
      exists: true,
      ok: Boolean(body.ok),
      path: dryRunPath,
      totalDocuments: Number(body.totalDocuments || 0),
      warningCount: Number(body.warningCount || 0),
      updatedAt: stat.mtime.toISOString()
    };
  } catch (err) {
    return {
      exists: false,
      ok: false,
      path: dryRunPath,
      totalDocuments: 0,
      warningCount: 0,
      updatedAt: null
    };
  }
}

function getLedgerFileInfo(ledgerPath) {
  try {
    const stat = fs.statSync(ledgerPath);
    return {
      exists: true,
      sizeBytes: stat.size,
      updatedAt: stat.mtime.toISOString()
    };
  } catch (err) {
    return {
      exists: false,
      sizeBytes: 0,
      updatedAt: null
    };
  }
}

function getDriverStatus(targetBackend) {
  if (targetBackend !== 'mongodb') {
    return {
      available: false,
      packageName: targetBackend,
      detail: `${targetBackend} driver check is not configured yet.`
    };
  }

  try {
    require.resolve('mongodb');
    return {
      available: true,
      packageName: 'mongodb',
      detail: 'mongodb driver is installed.'
    };
  } catch (err) {
    return {
      available: false,
      packageName: 'mongodb',
      detail: 'mongodb driver is not installed yet.'
    };
  }
}

function buildChecks({ dbUri, driver, consistency, dryRun, ledgerFile, switchRequested, mongoObservation }) {
  return [
    {
      key: 'ledger_snapshot_ready',
      label: 'JSON rollback snapshot',
      ok: ledgerFile.exists && ledgerFile.sizeBytes > 0,
      severity: 'critical',
      detail: ledgerFile.exists ? 'ledger.json rollback snapshot is present.' : 'ledger.json is missing.'
    },
    {
      key: 'dry_run_ready',
      label: 'Backfill dry-run',
      ok: dryRun.exists && dryRun.totalDocuments > 0,
      severity: 'critical',
      detail: dryRun.exists ? `${dryRun.totalDocuments} documents mapped.` : 'Run ledger DB dry-run first.'
    },
    {
      key: 'shadow_consistency',
      label: 'Shadow consistency',
      ok: Boolean(consistency.ok),
      severity: 'critical',
      detail: consistency.ok ? 'JSON and shadow rows match.' : `${consistency.issueCount || 0} shadow issue(s) detected.`
    },
    {
      key: 'db_uri_configured',
      label: 'DB URI configured',
      ok: Boolean(dbUri),
      severity: 'required',
      detail: dbUri ? 'DB URI is configured without exposing the secret.' : 'No DB URI env value is configured.'
    },
    {
      key: 'db_driver_available',
      label: 'DB driver available',
      ok: Boolean(driver.available),
      severity: 'required',
      detail: driver.detail
    },
    {
      key: 'mongo_observation_ready',
      label: 'Mongo observation compare',
      ok: Boolean(mongoObservation && mongoObservation.compare && mongoObservation.compare.ok),
      severity: 'critical',
      detail: mongoObservation && mongoObservation.compare && mongoObservation.compare.ok ? String(mongoObservation.compare.totalActual || 0) + ' observation docs match JSON.' : 'Run Mongo observation sync and compare first.'
    },
    {
      key: 'observation_window_ready',
      label: 'Observation window ready',
      ok: Boolean(mongoObservation && mongoObservation.window && mongoObservation.window.ok),
      severity: 'critical',
      detail: mongoObservation && mongoObservation.window && mongoObservation.window.ok ? String(mongoObservation.window.consecutiveOk || 0) + ' consecutive OK samples.' : 'Run a longer Mongo observation window first.'
    },
    {
      key: 'read_switch_locked',
      label: 'Read backend switch locked',
      ok: !switchRequested,
      severity: 'safety',
      detail: switchRequested ? 'Read switch was requested before readiness is complete.' : 'Read backend remains on JSON.'
    }
  ];
}

function getStatus() {
  const storageInfo = persistentStore.getStorageInfo ? persistentStore.getStorageInfo() : {};
  const ledgerPath = storageInfo.ledgerPath || persistentStore.getLedgerPath();
  const dataDir = storageInfo.dataDir || path.dirname(ledgerPath);
  const targetBackend = process.env.AISFOR_CHAIN_LEDGER_TARGET_BACKEND || 'mongodb';
  const dbUri = getDbUri();
  const driver = getDriverStatus(targetBackend);
  const consistency = ledgerConsistencyService.getStatus();
  const dryRun = getDryRunInfo(dataDir);
  const ledgerFile = getLedgerFileInfo(ledgerPath);
  const switchRequested = process.env.AISFOR_CHAIN_LEDGER_BACKEND === targetBackend || process.env.AISFOR_CHAIN_LEDGER_READ_BACKEND === targetBackend;
  const mongoObservation = storageInfo.mongoObservation || {};
  const checks = buildChecks({ dbUri, driver, consistency, dryRun, ledgerFile, switchRequested, mongoObservation });
  const okCount = checks.filter((row) => row.ok).length;
  const criticalOk = checks.filter((row) => row.severity === 'critical').every((row) => row.ok);
  const canAttachWriter = Boolean(dbUri) && driver.available && criticalOk;
  const observationWindowOk = Boolean(mongoObservation.window && mongoObservation.window.ok);
  const approval = readSwitchApprovalService.getApproval();
  const readSwitchApproved = process.env.AISFOR_CHAIN_LEDGER_READ_SWITCH_APPROVED === '1' || approval.approved;
  const readSwitchPreflightOk = canAttachWriter && observationWindowOk && !switchRequested;
  const canSwitchReads = Boolean(readSwitchPreflightOk && readSwitchApproved);

  return {
    targetBackend,
    dbUriConfigured: Boolean(dbUri),
    uriPreview: maskValue(dbUri),
    driverAvailable: driver.available,
    driverPackage: driver.packageName,
    shadowConsistent: Boolean(consistency.ok),
    dryRunReady: dryRun.exists && dryRun.totalDocuments > 0,
    rollbackReady: ledgerFile.exists && ledgerFile.sizeBytes > 0,
    switchRequested,
    mongoObservationReady: Boolean(mongoObservation.compare && mongoObservation.compare.ok),
    observationWindowReady: observationWindowOk,
    mongoObservation: {
      requested: Boolean(mongoObservation.requested),
      writeEnabled: Boolean(mongoObservation.writeEnabled),
      totalDocuments: mongoObservation.totalDocuments || 0,
      compareOk: Boolean(mongoObservation.compare && mongoObservation.compare.ok),
      compareExpected: mongoObservation.compare ? mongoObservation.compare.totalExpected || 0 : 0,
      compareActual: mongoObservation.compare ? mongoObservation.compare.totalActual || 0 : 0,
      failedCollections: mongoObservation.compare ? mongoObservation.compare.failedCollections || 0 : 0,
      windowOk: observationWindowOk,
      windowSamples: mongoObservation.window ? mongoObservation.window.sampleCount || 0 : 0,
      consecutiveOk: mongoObservation.window ? mongoObservation.window.consecutiveOk || 0 : 0,
      failedSamples: mongoObservation.window ? mongoObservation.window.failedSamples || 0 : 0
    },
    canAttachWriter,
    readSwitchPreflightOk,
    readSwitchApproved,
    readSwitchApproval: approval,
    canSwitchReads,
    switchLockReason: canSwitchReads ? 'Manual approval is recorded. Read switch can be requested explicitly.' : (readSwitchPreflightOk ? 'Technical preflight is ready, but manual read-switch approval is still required.' : 'Read switch still requires completed preflight checks.'),
    score: Math.round((okCount / checks.length) * 100),
    checks,
    dryRun,
    consistency: {
      ok: Boolean(consistency.ok),
      issueCount: consistency.issueCount || 0,
      totalSourceRows: consistency.totalSourceRows || 0,
      totalShadowRows: consistency.totalShadowRows || 0
    },
    ledgerFile,
    updatedAt: new Date().toISOString()
  };
}

module.exports = {
  getStatus
};



