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

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

const root = path.join(__dirname, '..');
const topology = JSON.parse(fs.readFileSync(path.join(root, 'runtime', 'plais-qbft-canonical-migration', 'topology.json'), 'utf8'));
const runtime = JSON.parse(fs.readFileSync(path.join(root, 'runtime', 'plais-validator-runtime', 'manifest.json'), 'utf8'));
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 deviceIdentity(node = {}) {
  return String(node.metadata?.syncDeviceId || node.metadata?.aisforNodeId || node.nodeId || node.id || '').trim();
}

function remoteValidatorHostEvidence(canonicalBlockNumber = 0) {
  const nodes = persistentStore.readState().nodes || [];
  const now = Date.now();
  const rows = topology.validators.map(target => {
    const node = latestNodeForTarget(nodes, target);
    const meta = node?.metadata || {};
    const heartbeatAgeMs = now - new Date(node?.lastHeartbeatAt || 0).getTime();
    const runtimeAgeMs = now - new Date(meta.validatorRuntimeCheckedAt || 0).getTime();
    const identity = deviceIdentity(node);
    const permittedReportedLag = 10 + Math.ceil(Math.max(0, runtimeAgeMs) / 2000);
    const ready = node?.status === 'active'
      && heartbeatAgeMs >= 0 && heartbeatAgeMs <= 3 * 60_000
      && supportedClientVersions.has(node?.clientVersion)
      && meta.validatorRuntimeSchema === 'plais-validator-runtime-deployment-status/v1'
      && meta.validatorRuntimeState === 'validator-active'
      && meta.validatorRuntimeArtifactSha256 === runtime.sha256
      && meta.validatorRuntimeDeviceKeyId === meta.provisioningKeyId
      && Number(meta.validatorRuntimeProcessPid || 0) > 0
      && Number(meta.validatorRuntimePeerCount || 0) >= 1
      && Number(meta.validatorRuntimeGapBlocks || 0) <= 10
      && Number(meta.validatorRuntimeBlockNumber || 0) >= Math.max(1, Number(canonicalBlockNumber || 0) - permittedReportedLag)
      && meta.validatorTunnelSchema === 'plais-validator-p2p-tunnel/v1'
      && ['direct-peers-applied', 'direct-lan-quorum-with-tunnel-fallback-applied']
        .includes(meta.validatorTunnelState)
      && /^[a-f0-9]{64}$/.test(String(meta.validatorTunnelTopologyHash || ''))
      && runtimeAgeMs >= 0 && runtimeAgeMs <= 3 * 60_000
      && Boolean(identity);
    return {
      nodeName: target.nodeName,
      pcName: target.pcName,
      deviceIdentity: identity,
      blockNumber: Number(meta.validatorRuntimeBlockNumber || 0),
      evidenceAgeSec: Number.isFinite(runtimeAgeMs) ? Math.max(0, Math.round(runtimeAgeMs / 1000)) : null,
      permittedReportedLag,
      ready
    };
  });
  const identities = rows.filter(row => row.ready).map(row => row.deviceIdentity);
  return {
    rows,
    activeCount: rows.filter(row => row.ready).length,
    distinctDeviceCount: new Set(identities).size,
    complete: rows.every(row => row.ready) && new Set(identities).size === topology.validators.length
  };
}

async function main() {
  const status = await candidateNetworkService.refreshStatus();
  const canonical = status.canonical || {};
  const remote = remoteValidatorHostEvidence(canonical.blockNumber);
  const canonicalPeerIdentitiesComplete = Number(canonical.validatorPeerIdentityCount || 0) >= topology.validators.length;
  const signedRemoteEvidenceComplete = remote.complete;
  const centralHostCount = signedRemoteEvidenceComplete
    ? 0
    : (Number(canonical.independentValidatorHostCount || 0) > 0 ? 1 : 0);
  const actual = remote.distinctDeviceCount + centralHostCount;
  const required = Number(CANONICAL_LEDGER_CONFIG.requiredIndependentHosts || 4);
  const ok = canonical.online === true
    && (signedRemoteEvidenceComplete || canonical.hostEvidenceComplete === true)
    && actual >= required;
  console.log(JSON.stringify({
    ok,
    mode: signedRemoteEvidenceComplete
      ? 'signed-remote-runtime-and-live-validator-identity-evidence'
      : 'live-admin-peers-validator-identity-evidence',
    chainId: canonical.chainId,
    blockNumber: canonical.blockNumber,
    validatorCount: canonical.validatorCount,
    validatorPeerIdentityCount: canonical.validatorPeerIdentityCount,
    independentValidatorHostCount: actual,
    requiredIndependentHosts: required,
    hostEvidenceComplete: signedRemoteEvidenceComplete || canonical.hostEvidenceComplete,
    signedRemoteEvidence: {
      complete: signedRemoteEvidenceComplete,
      activeCount: remote.activeCount,
      distinctDeviceCount: remote.distinctDeviceCount,
      centralFallbackHostCount: centralHostCount,
      canonicalPeerIdentitiesComplete,
      rows: remote.rows
    },
    checkedAt: canonical.checkedAt
  }, null, 2));
  if (!ok) process.exitCode = 1;
}

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