const fs = require('node:fs');
const path = require('node:path');
const cp = require('node:child_process');

const backendDir = path.resolve(__dirname, '..');
const repoDir = path.resolve(backendDir, '..', '..');
const frontendDir = path.join(repoDir, 'packages', 'frontend');
const stageDir = path.join(backendDir, 'build', 'pkg-stage');
const outputDir = path.join(backendDir, 'build', 'pkg-output');

function run(command, cwd, env) {
  cp.execFileSync(
    process.platform === 'win32' ? 'cmd.exe' : 'bash',
    process.platform === 'win32' ? ['/c', command] : ['-lc', command],
    { cwd, stdio: 'inherit', env: { ...process.env, ...env } }
  );
}

function assertStage(dir) {
  const required = [
    path.join(dir, 'public', 'index.html'),
    path.join(dir, 'dist', 'index.js'),
  ];
  for (const file of required) {
    if (!fs.existsSync(file)) {
      throw new Error(`缺少 staging 文件: ${file}`);
    }
  }
}

// Clean
fs.rmSync(stageDir, { recursive: true, force: true });
fs.rmSync(outputDir, { recursive: true, force: true });
fs.mkdirSync(stageDir, { recursive: true });

// Build frontend (override API base URL to use same-origin for installer)
console.log('[1/6] Building frontend...');
run('pnpm build', frontendDir, { VITE_API_BASE_URL: '', VITE_COOL_EPS_ENABLE: 'true' });

// Build backend
console.log('[2/6] Building backend...');
run('npm run build', backendDir);

// Stage files
console.log('[3/6] Staging files...');
fs.cpSync(path.join(backendDir, 'bootstrap.js'), path.join(stageDir, 'bootstrap.js'));
fs.cpSync(path.join(backendDir, 'dist'), path.join(stageDir, 'dist'), { recursive: true });
fs.cpSync(path.join(backendDir, 'public'), path.join(stageDir, 'public'), { recursive: true });
// Overlay frontend dist onto public (frontend index.html replaces backend welcome page)
fs.cpSync(path.join(frontendDir, 'dist'), path.join(stageDir, 'public'), { recursive: true, force: true });
if (fs.existsSync(path.join(backendDir, 'typings'))) {
  fs.cpSync(path.join(backendDir, 'typings'), path.join(stageDir, 'typings'), { recursive: true });
}
if (fs.existsSync(path.join(backendDir, 'src', 'locales'))) {
  fs.mkdirSync(path.join(stageDir, 'src'), { recursive: true });
  fs.cpSync(path.join(backendDir, 'src', 'locales'), path.join(stageDir, 'src', 'locales'), { recursive: true });
}

// Generate staging package.json
console.log('[4/6] Generating staging package.json...');
const backendPkg = require(path.join(backendDir, 'package.json'));
const stagePkg = {
  name: backendPkg.name,
  version: backendPkg.version,
  private: true,
  overrides: backendPkg.overrides || {},
  dependencies: backendPkg.dependencies,
  bin: './bootstrap.js',
  pkg: {
    scripts: ['dist/**/*.js', 'node_modules/**/*.mjs'],
    assets: [
      'public/**/*',
      'typings/**/*',
      'src/locales/**/*',
      'node_modules/@img/sharp-win32-x64/**/*',
      'node_modules/@img/colour/**/*',
      'node_modules/better-sqlite3/build/Release/*.node',
      'node_modules/@msgpackr-extract/msgpackr-extract-win32-x64/*.node',
      'node_modules/@napi-rs/canvas-win32-x64-msvc/*.node',
    ],
    targets: ['node20-win-x64'],
    outputPath: outputDir,
  },
};
fs.writeFileSync(path.join(stageDir, 'package.json'), JSON.stringify(stagePkg, null, 2));

// Install deps
console.log('[5/6] Installing production dependencies...');
run('npm install --production --install-strategy=hoisted --legacy-peer-deps', stageDir);

// Patch generator-function
const gfPkg = path.join(stageDir, 'node_modules', 'generator-function', 'package.json');
if (fs.existsSync(gfPkg)) {
  const gf = JSON.parse(fs.readFileSync(gfPkg, 'utf8'));
  if (gf.exports && gf.exports['.']) {
    gf.exports['.'] = './index.js';
    fs.writeFileSync(gfPkg, JSON.stringify(gf, null, 2));
    console.log('  Patched generator-function exports');
  }
}

// Run pkg
console.log('[6/6] Running pkg...');
assertStage(stageDir);
fs.mkdirSync(outputDir, { recursive: true });
run('npx @yao-pkg/pkg@6.14.1 .', stageDir);

// Validate
console.log('\n=== Build complete ===');
const exePath = path.join(outputDir, 'backend.exe');
if (fs.existsSync(exePath)) {
  const stat = fs.statSync(exePath);
  console.log(`Output: ${exePath} (${(stat.size / 1024 / 1024).toFixed(1)} MB)`);
}
