require('../config/loadEnv')();

const fs = require('fs');
const path = require('path');
const persistentStore = require('../services/persistentStore');
const { matchesValidatorTarget } = require('../services/validatorTargetBindingService');

const projectRoot = path.join(__dirname, '..');
const topologyPath = path.join(projectRoot, 'runtime', 'plais-qbft-canonical-migration', 'topology.json');
const supportedClientVersions = new Set([
  '1.4.30-validator-tunnel-approval-retry-single-install-locked',
  '1.4.31-validator-envelope-identity-rotation-single-install-locked',
  '1.4.32-validator-bridge-fallback-single-install-locked',
  '1.4.33-validator-presync-bridge-recovery-single-install-locked',
  '1.4.34-validator-runtime-diagnostic-single-install-locked',
  '1.4.35-validator-runtime-worker-guard-single-install-locked',
  '1.4.36-validator-memory-safe-single-install-locked',
  '1.4.37-validator-rpc-ready-recovery-single-install-locked',
  '1.4.38-validator-startup-monitor-single-install-locked',
  '1.4.39-validator-error-retention-single-install-locked',
  '1.4.40-validator-exit-code-diagnostic-single-install-locked',
  '1.4.41-validator-complete-exit-diagnostic-single-install-locked',
  '1.4.42-validator-dll-basename-diagnostic-single-install-locked',
  '1.4.43-validator-app-local-vc-runtime-single-install-locked',
  '1.4.44-validator-snap-presync-single-install-locked',
  '1.4.45-validator-activation-diagnostic-single-install-locked',
  '1.4.46-validator-failure-retention-single-install-locked',
  '1.4.47-validator-worker-lock-identity-single-install-locked',
  '1.4.48-validator-runtime-pid-identity-single-install-locked',
  '1.4.49-validator-tunnel-state-preservation-single-install-locked',
  '1.4.50-validator-worker-race-single-install-locked',
  '1.4.51-validator-orphan-worker-cleanup-single-install-locked',
  '1.4.52-validator-existing-besu-adoption-single-install-locked',
  '1.4.53-validator-static-topology-reconcile-single-install-locked',
  '1.4.54-validator-preserved-full-bridge-recovery-single-install-locked',
  '1.4.55-validator-tunnel-approval-refresh-single-install-locked',
  '1.4.56-validator-directed-tunnel-topology-single-install-locked',
  '1.4.57-validator-loopback-tunnel-peer-diagnostic-single-install-locked',
  '1.4.58-validator-launch-config-reconcile-single-install-locked',
  '1.4.59-validator-p2p-disconnect-diagnostic-single-install-locked',
  '1.4.60-validator-unique-advertised-endpoint-single-install-locked',
  '1.4.61-validator-active-data-path-reconcile-single-install-locked',
  '1.4.62-validator-approved-consensus-restart-single-install-locked',
  '1.4.63-validator-qbft-consensus-diagnostic-single-install-locked',
  '1.4.107-quick-tunnel-discovery-default-off-locked',
  '1.4.109-runtime-heartbeat-null-stale-fallback-default-off-locked',
  '1.4.110-data-replica-readiness-attestation-default-off-locked',
  '1.4.111-validator-node-mode-recovery-single-install-locked'
  ,'1.4.112-validator-control-error-attestation-single-install-locked'
  ,'1.4.113-validator-control-evidence-priority-single-install-locked'
  ,'1.4.114-validator-runtime-evidence-priority-single-install-locked'
  ,'1.4.115-validator-active-tunnel-restart-stability-single-install-locked'
  ,'1.4.116-validator-active-tunnel-restart-stability-single-install-locked'
  ,'1.4.117-validator-direct-lan-quorum-tunnel-fallback-locked'
  ,'1.4.118-validator-direct-lan-runtime-attestation-tunnel-fallback-locked'
  ,'1.4.119-validator-direct-nodeinfo-attestation-tunnel-fallback-locked'
  ,'1.4.120-validator-direct-attestation-refresh-tunnel-fallback-locked'
  ,'1.4.121-validator-direct-evidence-diagnostic-tunnel-fallback-locked'
  ,'1.4.122-validator-direct-offer-state-diagnostic-tunnel-fallback-locked'
  ,'1.4.123-validator-direct-control-heartbeat-diagnostic-tunnel-fallback-locked'
]);

function latestNodeForTarget(nodes, target) {
  return nodes
    .filter((node) => matchesValidatorTarget(node, target))
    .sort((left, right) => new Date(right.lastHeartbeatAt || 0) - new Date(left.lastHeartbeatAt || 0))[0] || null;
}

function main() {
  const topology = JSON.parse(fs.readFileSync(topologyPath, 'utf8'));
  const nodes = persistentStore.readState().nodes || [];
  const now = Date.now();
  const rows = (topology.validators || []).map((target) => {
    const node = latestNodeForTarget(nodes, target);
    const heartbeatAgeSec = node?.lastHeartbeatAt
      ? Math.max(0, Math.round((now - new Date(node.lastHeartbeatAt).getTime()) / 1000))
      : null;
    const metadata = node?.metadata || {};
    const receiptHash = String(metadata.chainProvisioningHash || '').trim().toLowerCase();
    const checks = {
      registered: Boolean(node),
      active: node?.status === 'active' && Number.isFinite(heartbeatAgeSec) && heartbeatAgeSec <= 300,
      clientReady: supportedClientVersions.has(String(node?.clientVersion || '')),
      deviceEncryptionKeyReady: metadata.provisioningKeyAlgorithm === 'rsa-oaep-sha256-dpapi-current-user-v1'
        && /^[a-f0-9]{64}$/.test(String(metadata.provisioningKeyId || ''))
        && Boolean(metadata.provisioningPublicKey),
      schemaVerified: metadata.chainProvisioningSchema === 'plais-node-provisioning/v1',
      receiptVerified: /^[a-f0-9]{64}$/.test(receiptHash),
      roleVerified: metadata.chainProvisioningRole === 'validator-candidate',
      approvalStateVerified: metadata.chainProvisioningState === 'awaiting-operator-approval'
    };
    return {
      pcName: target.pcName,
      nodeName: target.nodeName || target.name || '',
      clientVersion: String(node?.clientVersion || ''),
      heartbeatAgeSec,
      checks,
      ready: Object.values(checks).every(Boolean)
    };
  });
  const readyForApproval = rows.length > 0 && rows.every((row) => row.ready);
  console.log(JSON.stringify({
    ok: true,
    mode: 'sync-client-read-only-validator-provisioning',
    supportedClientVersions: [...supportedClientVersions],
    readyForApproval,
    controls: {
      packageInstallAllowed: false,
      validatorKeyImportAllowed: false,
      validatorStartAllowed: false
    },
    rows,
    checkedAt: new Date().toISOString()
  }, null, 2));
  if (!readyForApproval) process.exitCode = 2;
}

try {
  main();
} catch (error) {
  console.error(JSON.stringify({ ok: false, error: error.message }, null, 2));
  process.exit(1);
}
