const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const projectRoot = path.join(__dirname, '..');
const runtimeRoot = path.join(projectRoot, 'runtime');
const besuDirectoryName = 'besu-26.6.1';
const jdkDirectoryName = 'jdk-25.0.3+9';
const besuPath = path.join(runtimeRoot, besuDirectoryName);
const jdkPath = path.join(runtimeRoot, jdkDirectoryName);
const artifactVersion = 'plais-validator-runtime-besu-26.6.1-jdk-25.0.3-v1';
const publicRoot = path.resolve(process.env.PLAIS_VALIDATOR_RUNTIME_PUBLIC_ROOT || 'E:\\aisfor\\public\\downloads');
const outputPath = path.join(publicRoot, `${artifactVersion}.zip`);
const manifestPath = path.join(runtimeRoot, 'plais-validator-runtime', 'manifest.json');

function sha256File(filePath) {
  const hash = crypto.createHash('sha256');
  const fd = fs.openSync(filePath, 'r');
  const buffer = Buffer.allocUnsafe(4 * 1024 * 1024);
  try {
    while (true) {
      const read = fs.readSync(fd, buffer, 0, buffer.length, null);
      if (!read) break;
      hash.update(buffer.subarray(0, read));
    }
  } finally {
    fs.closeSync(fd);
  }
  return hash.digest('hex');
}

function requireRuntime() {
  for (const required of [
    path.join(besuPath, 'lib'),
    path.join(jdkPath, 'bin', 'java.exe')
  ]) {
    if (!fs.existsSync(required)) throw new Error(`Validator runtime source is missing: ${required}`);
  }
}

function main() {
  requireRuntime();
  fs.mkdirSync(publicRoot, { recursive: true });
  fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
  const temporaryPath = `${outputPath}.tmp-${process.pid}.zip`;
  const stagingRoot = path.join(path.dirname(manifestPath), `build-staging-${process.pid}-${Date.now()}`);
  const stagingBesuPath = path.join(stagingRoot, besuDirectoryName);
  const stagingJdkPath = path.join(stagingRoot, jdkDirectoryName);
  if (fs.existsSync(temporaryPath)) fs.unlinkSync(temporaryPath);
  fs.mkdirSync(stagingRoot, { recursive: true });
  try {
    fs.cpSync(besuPath, stagingBesuPath, { recursive: true, errorOnExist: true });
    fs.cpSync(jdkPath, stagingJdkPath, { recursive: true, errorOnExist: true });
    const script = [
      "$ErrorActionPreference = 'Stop'",
      'Compress-Archive -LiteralPath @($env:PLAIS_BESU_PATH, $env:PLAIS_JDK_PATH) -DestinationPath $env:PLAIS_OUTPUT_PATH -CompressionLevel Optimal -Force'
    ].join('\n');
    const compressed = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {
      cwd: stagingRoot,
      encoding: 'utf8',
      windowsHide: true,
      maxBuffer: 4 * 1024 * 1024,
      env: {
        ...process.env,
        PLAIS_BESU_PATH: stagingBesuPath,
        PLAIS_JDK_PATH: stagingJdkPath,
        PLAIS_OUTPUT_PATH: temporaryPath
      }
    });
    if (compressed.status !== 0 || !fs.existsSync(temporaryPath)) {
      throw new Error(String(compressed.stderr || compressed.stdout || `Compress-Archive exited ${compressed.status}`).trim());
    }
  } finally {
    fs.rmSync(stagingRoot, { recursive: true, force: true });
  }
  if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath);
  fs.renameSync(temporaryPath, outputPath);
  const stat = fs.statSync(outputPath);
  const manifest = {
    schemaVersion: 'plais-validator-runtime-artifact/v1',
    version: artifactVersion,
    fileName: path.basename(outputPath),
    publicPath: `/public/downloads/${path.basename(outputPath)}`,
    bytes: stat.size,
    sha256: sha256File(outputPath),
    contents: {
      besuDirectoryName,
      jdkDirectoryName,
      privateKeysIncluded: false,
      chainConfigurationIncluded: false
    },
    createdAt: new Date().toISOString()
  };
  const tempManifest = `${manifestPath}.tmp-${process.pid}`;
  fs.writeFileSync(tempManifest, `${JSON.stringify(manifest, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
  fs.renameSync(tempManifest, manifestPath);
  console.log(JSON.stringify({ ok: true, outputPath, manifestPath, ...manifest }, null, 2));
}

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