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

const fs = require('fs');
const net = require('net');
const os = require('os');
const path = require('path');
const { spawn } = require('child_process');
const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');

const projectRoot = path.join(__dirname, '..');
const runtimeRoot = path.join(projectRoot, 'runtime');
const javaHome = path.join(runtimeRoot, 'jdk-25.0.3+9');
const besuRoot = path.join(runtimeRoot, 'besu-26.6.1');
const javaCommand = path.join(javaHome, 'bin', 'java.exe');
const besuClasspath = path.join(besuRoot, 'lib', '*');
const networkRoot = path.join(runtimeRoot, 'plais-qbft');
const networkPath = path.join(networkRoot, 'network.json');
const bridgeRoot = path.join(runtimeRoot, 'plais-qbft-public-bridge');
const configPath = path.join(bridgeRoot, 'config.json');
const dataPath = path.join(bridgeRoot, 'data');
const staticNodesPath = path.join(dataPath, 'static-nodes.json');

function fail(message) {
  console.error(message);
  process.exit(1);
}

function localIpv4Addresses() {
  return new Set(Object.values(os.networkInterfaces()).flatMap((rows) => (rows || []))
    .filter((row) => row.family === 'IPv4' && !row.internal)
    .map((row) => row.address));
}

if (!EVM_CANDIDATE_CONFIG.enabled) fail('PLAIS EVM candidate is disabled.');
if (!fs.existsSync(networkPath) || !fs.existsSync(configPath)) fail('QBFT public bridge configuration is missing.');

const network = JSON.parse(fs.readFileSync(networkPath, 'utf8'));
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
const advertisedHost = String(config.advertisedHost || '').trim();
const listenInterface = String(config.interface || '').trim();
const p2pPort = Number(config.p2pPort || 0);
const maxPeers = Math.max(4, Math.min(32, Number(config.maxPeers || 12)));
const allowedRemoteAddresses = Array.isArray(config.allowedRemoteAddresses)
  ? [...new Set(config.allowedRemoteAddresses.map((value) => String(value || '').trim()).filter(Boolean))]
  : [];

if (config.schemaVersion !== 'plais-qbft-public-bridge/v1' || config.enabled !== true) {
  fail('QBFT public bridge is not explicitly enabled.');
}
if (net.isIP(advertisedHost) !== 4 || !localIpv4Addresses().has(advertisedHost)) {
  fail('QBFT public bridge advertisedHost must be a local non-loopback IPv4 address.');
}
if (!['0.0.0.0', advertisedHost].includes(listenInterface)) fail('QBFT public bridge interface is unsafe.');
if (!Number.isInteger(p2pPort) || p2pPort < 1024 || p2pPort > 65535) fail('QBFT public bridge port is invalid.');
if (allowedRemoteAddresses.length < 1 || allowedRemoteAddresses.some((value) => net.isIP(value) !== 4)) {
  fail('QBFT public bridge requires an IPv4 firewall allowlist.');
}

fs.mkdirSync(dataPath, { recursive: true });
const staticNodes = (network.nodes || [])
  .filter((entry) => entry.role === 'validator')
  .map((entry) => {
    const publicKeyPath = path.join(networkRoot, entry.name, 'data', 'key.pub');
    if (!fs.existsSync(publicKeyPath)) fail(`Validator public key is missing: ${entry.name}`);
    const publicKey = fs.readFileSync(publicKeyPath, 'utf8').trim().replace(/^0x/, '');
    if (!/^[a-f0-9]{128}$/i.test(publicKey)) fail(`Validator public key is invalid: ${entry.name}`);
    return `enode://${publicKey}@127.0.0.1:${entry.p2pPort}`;
  });
if (staticNodes.length !== Number(network.validatorCount || 4)) fail('Canonical validator list is incomplete.');
fs.writeFileSync(staticNodesPath, `${JSON.stringify(staticNodes, null, 2)}\n`);

if (process.argv.includes('--check')) {
  console.log(JSON.stringify({
    ok: true,
    state: 'PUBLIC_BRIDGE_CONFIG_READY',
    chainId: network.chainId,
    advertisedHost,
    listenInterface,
    p2pPort,
    validatorPeerCount: staticNodes.length,
    firewallAllowlistCount: allowedRemoteAddresses.length,
    rpcEnabled: false
  }, null, 2));
  process.exit(0);
}

const args = [
  `--data-path=${dataPath}`,
  `--genesis-file=${network.genesisFile}`,
  '--profile=ENTERPRISE',
  '--sync-mode=FULL',
  '--data-storage-format=BONSAI',
  '--snapsync-server-enabled=true',
  `--p2p-host=${advertisedHost}`,
  `--p2p-interface=${listenInterface}`,
  `--p2p-port=${p2pPort}`,
  `--max-peers=${maxPeers}`,
  '--discovery-enabled=false',
  '--nat-method=NONE',
  '--min-gas-price=0',
  '--rpc-http-enabled=false',
  `--static-nodes-file=${staticNodesPath}`,
  '--logging=INFO'
];

console.log(`Starting PLAIS public P2P bridge ${advertisedHost}:${p2pPort}; firewall allowlist=${allowedRemoteAddresses.join(',')}`);
const child = spawn(javaCommand, [
  '--enable-native-access=ALL-UNNAMED',
  '-Xms128m',
  '-Xmx512m',
  '-cp', besuClasspath,
  'org.hyperledger.besu.Besu',
  ...args
], {
  cwd: besuRoot,
  env: { ...process.env, JAVA_HOME: javaHome },
  stdio: 'inherit',
  shell: false,
  windowsHide: true
});

child.on('exit', (code, signal) => {
  console.log(`PLAIS public P2P bridge stopped code=${code} signal=${signal || '-'}`);
  process.exit(code == null ? 1 : code);
});
child.on('error', (error) => {
  console.error(error);
  process.exit(1);
});

function shutdown(signal) {
  if (!child.killed) child.kill(signal);
}
process.on('SIGINT', () => shutdown('SIGINT'));
process.on('SIGTERM', () => shutdown('SIGTERM'));
