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

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

const CONFIRMATION = 'ACTIVATE DIRECT VALIDATOR QUORUM FROM CONTROL EVIDENCE';
const confirm = String(process.argv.find(value => value.startsWith('--confirm=')) || '').slice(10);
if (confirm !== CONFIRMATION) {
  throw new Error('Exact direct quorum activation confirmation is required.');
}

const EXPECTED_VERSION = '1.4.123-validator-direct-control-heartbeat-diagnostic-tunnel-fallback-locked';
const EXPECTED_NODES = ['validator-1', 'validator-2', 'validator-3'];
const historyRoot = path.join(path.dirname(service.paths.approvalPath), 'approval-history');

function sha256Bytes(bytes) {
  return crypto.createHash('sha256').update(bytes).digest('hex');
}

function readEvidenceArchives() {
  return fs.readdirSync(historyRoot)
    .filter(name => name.endsWith('-direct-control-attested.json'))
    .map(name => {
      const filePath = path.join(historyRoot, name);
      const bytes = fs.readFileSync(filePath);
      return { name, filePath, bytes, sha256: sha256Bytes(bytes), value: JSON.parse(bytes) };
    });
}

if (fs.existsSync(service.paths.approvalPath)) {
  throw new Error('An active validator approval exists; quorum activation is not allowed.');
}
const topologyBytes = fs.readFileSync(service.paths.topologyPath);
const topology = JSON.parse(topologyBytes);
const stagedTopologySha256 = sha256Bytes(topologyBytes);
const direct = service.getDirectQuorum(topology);
if (!direct || !['STAGED', 'ACTIVE'].includes(direct.state)
  || JSON.stringify(direct.targetNodeNames) !== JSON.stringify(EXPECTED_NODES)) {
  throw new Error('The exact staged or active validator direct quorum is required.');
}

const allArchives = readEvidenceArchives();
const nodes = persistentStore.readState().nodes || [];
const activationNodes = [];
const approvalIds = new Set();
const blockNumbers = [];
const now = Date.now();

for (const target of direct.targets) {
  const matches = allArchives
    .filter(row => row.value?.nodeName === target.nodeName)
    .sort((a, b) => new Date(b.value?.archivedAt || 0) - new Date(a.value?.archivedAt || 0));
  const archive = matches[0];
  const attestation = archive?.value;
  const evidence = attestation?.evidence || {};
  if (!archive
    || attestation.schemaVersion !== 'plais-validator-tunnel-migration-approval/v1'
    || attestation.action !== 'ATTEST_EXISTING_SINGLE_VALIDATOR_DIRECT_LAN'
    || attestation.archiveStatus !== 'DIRECT_CONTROL_ATTESTED'
    || attestation.nodeName !== target.nodeName
    || attestation.topologySha256 !== stagedTopologySha256
    || attestation.controls?.directLanQuorumAttestation !== true
    || attestation.controls?.validatorProcessRestartAllowed !== false
    || attestation.controls?.validatorKeyReadForIdentityCheckOnly !== true
    || attestation.controls?.validatorKeyExportAllowed !== false
    || attestation.controls?.publicRpcAllowed !== false
    || attestation.controls?.externalTransactionAllowed !== false
    || attestation.controls?.operatorSigningAllowed !== false
    || evidence.schemaVersion !== 'plais-validator-direct-control-evidence/v1'
    || evidence.nodeName !== target.nodeName
    || evidence.pcName !== target.pcName
    || evidence.clientVersion !== EXPECTED_VERSION
    || evidence.topologySha256 !== stagedTopologySha256
    || evidence.managerState !== 'applied'
    || evidence.runtimeState !== 'validator-active'
    || evidence.directStatic !== true
    || evidence.processRunning !== true
    || evidence.offerAuditObserved !== true
    || evidence.authenticatedHeartbeatObserved !== true
    || Number(evidence.processPid || 0) <= 0
    || Number(evidence.peerCount || 0) < 2
    || Number(evidence.blockNumber || 0) <= 0) {
    throw new Error(`Bound direct-control evidence is invalid: ${target.nodeName}.`);
  }
  if (approvalIds.has(attestation.approvalId)) {
    throw new Error('Direct-control approval IDs must be unique.');
  }

  const node = nodes.filter(row => matchesValidatorTarget(row, target))
    .sort((a, b) => new Date(b.lastHeartbeatAt || 0) - new Date(a.lastHeartbeatAt || 0))[0];
  const meta = node?.metadata || {};
  const heartbeatAt = new Date(node?.lastHeartbeatAt || 0).getTime();
  const runtimeCheckedAt = new Date(meta.validatorRuntimeCheckedAt || 0).getTime();
  if (node?.status !== 'active'
    || node?.clientVersion !== EXPECTED_VERSION
    || !Number.isFinite(heartbeatAt) || now - heartbeatAt < 0 || now - heartbeatAt > 3 * 60_000
    || !Number.isFinite(runtimeCheckedAt) || now - runtimeCheckedAt < 0 || now - runtimeCheckedAt > 3 * 60_000
    || meta.validatorRuntimeState !== 'validator-active'
    || Number(meta.validatorRuntimeProcessPid || 0) !== Number(evidence.processPid)
    || Number(meta.validatorRuntimePeerCount || 0) < 2
    || Number(meta.validatorRuntimeBlockNumber || 0) < Number(evidence.blockNumber)) {
    throw new Error(`Fresh runtime no longer matches the archived evidence: ${target.nodeName}.`);
  }

  approvalIds.add(attestation.approvalId);
  blockNumbers.push(Number(meta.validatorRuntimeBlockNumber));
  activationNodes.push({
    nodeName: target.nodeName,
    pcName: target.pcName,
    approvalId: attestation.approvalId,
    processPid: Number(evidence.processPid),
    attestedBlockNumber: Number(evidence.blockNumber),
    currentBlockNumber: Number(meta.validatorRuntimeBlockNumber),
    archiveFile: archive.name,
    archiveSha256: archive.sha256,
    attestedAt: evidence.observedAt
  });
}

if (Math.max(...blockNumbers) - Math.min(...blockNumbers) > 20) {
  throw new Error('Direct validator quorum block convergence is incomplete.');
}

const activatedAt = new Date().toISOString();
const nextTopology = {
  ...topology,
  directQuorum: {
    ...topology.directQuorum,
    state: 'ACTIVE',
    activatedAt,
    activationEvidence: {
      schemaVersion: 'plais-validator-direct-quorum-activation-evidence/v1',
      stagedTopologySha256,
      nodes: activationNodes
    }
  },
  updatedAt: activatedAt
};
const temporaryPath = `${service.paths.topologyPath}.${process.pid}.tmp`;
fs.writeFileSync(temporaryPath, `${JSON.stringify(nextTopology, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
fs.renameSync(temporaryPath, service.paths.topologyPath);

console.log(JSON.stringify({
  ok: true,
  status: direct.state === 'ACTIVE'
    ? 'DIRECT_LAN_QUORUM_ACTIVE_CONTROL_EVIDENCE_REFRESHED'
    : 'DIRECT_LAN_QUORUM_ACTIVE_FROM_BOUND_CONTROL_EVIDENCE',
  targetNodeNames: direct.targetNodeNames,
  stagedTopologySha256,
  activationNodes,
  blockRange: { minimum: Math.min(...blockNumbers), maximum: Math.max(...blockNumbers) },
  activatedAt
}, null, 2));
