import { cp, mkdir, rm, stat, writeFile } from 'node:fs/promises';
import { spawn } from 'node:child_process';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const scriptDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(scriptDir, '..', '..');
const buildDir = join(scriptDir, 'build');
const publicDir = join(repoRoot, 'public', 'vendor', 'opencascade');
const image = process.env.OPENCASCADE_JS_DOCKER_IMAGE || 'donalffons/opencascade.js';
const yaml = 'opencascade.yml';

await mkdir(buildDir, { recursive: true });
await cp(join(scriptDir, yaml), join(buildDir, yaml));

const dockerArgs = [
  'run',
  '--rm',
  '-v',
  `${buildDir}:/src`,
  '-u',
  `${process.getuid?.() ?? 1000}:${process.getgid?.() ?? 1000}`,
  image,
  yaml,
];

console.log(`building ${yaml} with ${image}`);
await run('docker', dockerArgs);

await stageArtifacts();

async function stageArtifacts() {
  const js = join(buildDir, 'opencascade.js');
  const wasm = join(buildDir, 'opencascade.wasm');

  await assertFile(js);
  await assertFile(wasm);

  await mkdir(publicDir, { recursive: true });

  await cp(js, join(publicDir, 'opencascade.js'));
  await cp(wasm, join(publicDir, 'opencascade.wasm'));

  await rm(join(publicDir, 'opencascade.step.js'), { force: true });
  await rm(join(publicDir, 'opencascade.step.wasm'), { force: true });
  await rm(join(publicDir, 'opencascade.step.worker.js'), { force: true });
  await rm(join(publicDir, 'opencascade.worker.js'), { force: true });

  await writeFile(
    join(publicDir, 'README.txt'),
    [
      'Local OpenCascade.js STEP conversion artifacts.',
      'Generated by tools/renderer-step/build-and-stage-opencascade.mjs.',
      'Single-threaded build: no pthread sidecar and no cross-origin isolation requirement.',
      'Generated/ignored; do not commit.',
      '',
    ].join('\n'),
  );

  console.log(`staged OpenCascade.js STEP artifacts into ${publicDir}`);
}

async function assertFile(path) {
  const info = await stat(path);
  if (!info.isFile() || info.size === 0) {
    throw new Error(`expected non-empty file at ${path}`);
  }
}

function run(command, args) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args, { stdio: 'inherit' });
    child.on('error', reject);
    child.on('close', (code, signal) => {
      if (code === 0) {
        resolve();
      } else {
        reject(new Error(`${command} exited with ${signal || code}`));
      }
    });
  });
}
