require('../config/loadEnv')();
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const { getAddress, isAddress, parseEther } = require('ethers');
const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');

const projectRoot = path.join(__dirname, '..');
const runtimeRoot = path.join(projectRoot, 'runtime');
const besuRoot = path.join(runtimeRoot, 'besu-26.6.1');
const javaHome = path.join(runtimeRoot, 'jdk-25.0.3+9');
const javaCommand = path.join(javaHome, 'bin', 'java.exe');
const besuClasspath = path.join(besuRoot, 'lib', '*');
const networkRoot = path.join(runtimeRoot, 'plais-qbft');
const generatedRoot = path.join(networkRoot, 'generated');
const validatorCount = EVM_CANDIDATE_CONFIG.validatorCount;

function requireRuntime(filePath, label) {
  if (!fs.existsSync(filePath)) throw new Error(`${label} is missing: ${filePath}`);
}

function readConfiguredOperatorAddress() {
  const address = String(process.env.PLAIS_EVM_OPERATOR_ADDRESS || '').trim();
  if (!isAddress(address)) {
    throw new Error('PLAIS_EVM_OPERATOR_ADDRESS is required; plaintext key generation is disabled.');
  }
  return getAddress(address);
}

function copyFile(from, to) {
  fs.mkdirSync(path.dirname(to), { recursive: true });
  fs.copyFileSync(from, to);
}

function main() {
  requireRuntime(path.join(besuRoot, 'lib'), 'Besu');
  requireRuntime(javaCommand, 'Temurin JDK');
  if (fs.existsSync(path.join(networkRoot, 'network.json'))) {
    throw new Error('QBFT network is already initialized; existing keys were left untouched.');
  }
  if (validatorCount < 4) throw new Error('QBFT requires at least four validators for one-node fault tolerance.');

  const operatorAddress = readConfiguredOperatorAddress();
  fs.mkdirSync(networkRoot, { recursive: true });
  const configPath = path.join(networkRoot, 'qbftConfigFile.json');
  const config = {
    genesis: {
      config: {
        chainId: EVM_CANDIDATE_CONFIG.chainId,
        berlinBlock: 0,
        londonBlock: 0,
        zeroBaseFee: true,
        qbft: {
          blockperiodseconds: 2,
          epochlength: 30000,
          requesttimeoutseconds: 4
        }
      },
      nonce: '0x0',
      timestamp: '0x0',
      gasLimit: '0x1c9c380',
      difficulty: '0x1',
      mixHash: '0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365',
      coinbase: '0x0000000000000000000000000000000000000000',
      alloc: {
        [operatorAddress.slice(2).toLowerCase()]: {
          balance: `0x${parseEther(EVM_CANDIDATE_CONFIG.genesisSupply).toString(16)}`
        }
      }
    },
    blockchain: { nodes: { generate: true, count: validatorCount } }
  };
  fs.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);

  const result = spawnSync(javaCommand, [
    '--enable-native-access=ALL-UNNAMED',
    '-Xms128m',
    '-Xmx512m',
    '-cp', besuClasspath,
    'org.hyperledger.besu.Besu',
    'operator', 'generate-blockchain-config',
    `--config-file=${configPath}`,
    `--to=${generatedRoot}`,
    '--private-key-file-name=key'
  ], {
    cwd: networkRoot,
    env: { ...process.env, JAVA_HOME: javaHome },
    encoding: 'utf8',
    shell: false
  });
  if (result.status !== 0) {
    process.stdout.write(result.stdout || '');
    process.stderr.write(result.stderr || '');
    throw new Error(`Besu network generation failed with status ${result.status}`);
  }

  const keyRoot = path.join(generatedRoot, 'keys');
  const keyDirs = fs.readdirSync(keyRoot, { withFileTypes: true })
    .filter((entry) => entry.isDirectory())
    .map((entry) => entry.name)
    .sort();
  if (keyDirs.length !== validatorCount) throw new Error('Unexpected validator key count.');

  const nodes = [];
  keyDirs.forEach((keyDir, index) => {
    const name = `validator-${index + 1}`;
    const dataPath = path.join(networkRoot, name, 'data');
    copyFile(path.join(keyRoot, keyDir, 'key'), path.join(dataPath, 'key'));
    copyFile(path.join(keyRoot, keyDir, 'key.pub'), path.join(dataPath, 'key.pub'));
    nodes.push({ name, role: 'validator', p2pPort: 30303 + index, address: keyDir });
  });
  fs.mkdirSync(path.join(networkRoot, 'rpc', 'data'), { recursive: true });
  nodes.push({ name: 'rpc', role: 'rpc', p2pPort: 30303 + validatorCount, rpcPort: EVM_CANDIDATE_CONFIG.rpcPort });

  const bootPublicKey = fs.readFileSync(path.join(networkRoot, 'validator-1', 'data', 'key.pub'), 'utf8')
    .trim()
    .replace(/^0x/, '');
  const network = {
    version: 1,
    client: 'besu-26.6.1',
    java: 'temurin-25.0.3+9',
    consensus: 'QBFT',
    chainId: EVM_CANDIDATE_CONFIG.chainId,
    validatorCount,
    operatorAddress,
    genesisFile: path.join(generatedRoot, 'genesis.json'),
    bootnode: `enode://${bootPublicKey}@127.0.0.1:30303`,
    nodes
  };
  fs.writeFileSync(path.join(networkRoot, 'network.json'), `${JSON.stringify(network, null, 2)}\n`);
  console.log(JSON.stringify({ ok: true, chainId: network.chainId, validators: validatorCount, operatorAddress }));
}

try {
  main();
} catch (error) {
  console.error(error.message);
  process.exit(1);
}
