#!/usr/bin/env node
/**
 * @file Pre-build guard.
 *
 * Ensures the desktop bundle has everything it needs before TypeScript
 * compiles. Specifically:
 *   1. The root repo's node_modules exists (Express + friends).
 *   2. The client has been built (client/dist exists). In production mode the
 *      Express server serves the SPA from client/dist; if it's missing the
 *      DMG would ship a 404-only dashboard.
 *   3. Asset PNGs exist (or we leave a clear warning — icons can be
 *      regenerated via scripts/build-icons.sh).
 *   4. The desktop-local better-sqlite3 native binary matches this machine's
 *      CPU architecture. A prior `electron-builder --mac --x64/--arm64` build
 *      rebuilds it for the target arch; left mismatched it breaks `desktop:dev`
 *      and `desktop:test` with ERR_DLOPEN_FAILED. We rebuild it if so.
 * @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
 */

const fs = require("node:fs");
const path = require("node:path");
const { spawnSync } = require("node:child_process");
const { hasBetterSqliteBinary, printNativeDepHelp } = require("./preflight");

const desktopRoot = path.resolve(__dirname, "..");
const repoRoot = path.resolve(__dirname, "..", "..");
const clientDist = path.join(repoRoot, "client", "dist");
const rootNodeModules = path.join(repoRoot, "node_modules");
const assets = path.join(desktopRoot, "assets");

function run(cmd, args, opts = {}) {
  // On Windows `npm`/`npx` are `.cmd` shims that `spawnSync` can only launch
  // through a shell; without this it fails with ENOENT. POSIX is unaffected.
  const result = spawnSync(cmd, args, {
    stdio: "inherit",
    shell: process.platform === "win32",
    ...opts,
  });
  if (result.status !== 0) {
    throw new Error(`${cmd} ${args.join(" ")} failed with exit ${result.status}`);
  }
}

if (!fs.existsSync(rootNodeModules)) {
  console.log("[prebuild] installing root dependencies…");
  run("npm", ["ci"], { cwd: repoRoot });
}

if (!fs.existsSync(clientDist) || !fs.existsSync(path.join(clientDist, "index.html"))) {
  console.log("[prebuild] building client (client/dist missing)…");
  run("npm", ["ci"], { cwd: path.join(repoRoot, "client") });
  run("npm", ["run", "build"], { cwd: repoRoot });
}

const trayIcon = path.join(assets, "tray-icon-Template.png");
if (!fs.existsSync(trayIcon)) {
  console.warn(
    "[prebuild] WARN: tray-icon-Template.png missing. Run `npm run build:icons` to regenerate from assets/icon.svg."
  );
}

// Heal a better-sqlite3 native binary left built for the wrong CPU arch by a
// prior `electron-builder --mac --x64/--arm64` run. Without this, `desktop:dev`
// and `desktop:test` fail to load the module (ERR_DLOPEN_FAILED) until the
// contributor manually re-runs `npm run desktop:install`.
if (process.platform === "darwin") {
  const bsNode = path.join(
    desktopRoot,
    "node_modules",
    "better-sqlite3",
    "build",
    "Release",
    "better_sqlite3.node"
  );
  if (fs.existsSync(bsNode)) {
    const desc = spawnSync("file", ["-b", bsNode], { encoding: "utf8" }).stdout || "";
    // A universal binary works on both arches; only act on a clear mismatch.
    const universal = /universal/i.test(desc);
    const wrongArch =
      !universal &&
      ((process.arch === "arm64" && !/arm64/.test(desc)) ||
        (process.arch === "x64" && !/x86_64/.test(desc)));
    if (wrongArch) {
      console.log(
        "[prebuild] better-sqlite3 is built for the wrong CPU arch (a prior DMG build left it that way) — rebuilding for this machine…"
      );
      run("npx", ["electron-builder", "install-app-deps"], { cwd: desktopRoot });
    }
  }
}

// The embedded server `require`s better-sqlite3 at boot; without its native
// binary the desktop app dies with a fatal dialog after compiling cleanly.
// Catch it here (a build-time, copy-pasteable failure) rather than at runtime.
if (!hasBetterSqliteBinary()) {
  printNativeDepHelp("The desktop-local better-sqlite3 native binary is missing.");
  process.exit(1);
}

console.log("[prebuild] ok");
