#!/usr/bin/env node

/**
 * OmniRoute — Postinstall Native Module Fix
 *
 * The npm package ships with a Next.js standalone build that includes
 * better-sqlite3 compiled for the build platform (Linux x64) inside
 * app/node_modules/. However, npm also installs better-sqlite3 as a
 * top-level dependency (in the root node_modules/), correctly compiled
 * for the user's platform.
 *
 * This script copies the correctly-built native binary from the root
 * into the standalone app directory — no rebuild or build tools needed.
 *
 * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/129
 * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/321
 * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/426
 */

import { existsSync, copyFileSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";

import { PUBLISHED_BUILD_PLATFORM, PUBLISHED_BUILD_ARCH } from "./native-binary-compat.mjs";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const ROOT = join(__dirname, "..");

const appBinary = join(
  ROOT,
  "app",
  "node_modules",
  "better-sqlite3",
  "build",
  "Release",
  "better_sqlite3.node"
);
const rootBinary = join(
  ROOT,
  "node_modules",
  "better-sqlite3",
  "build",
  "Release",
  "better_sqlite3.node"
);

if (!existsSync(join(ROOT, "app", "node_modules", "better-sqlite3"))) {
  process.exit(0);
}

const platformMatch =
  process.platform === PUBLISHED_BUILD_PLATFORM && process.arch === PUBLISHED_BUILD_ARCH;

if (platformMatch) {
  try {
    process.dlopen({ exports: {} }, appBinary);
    process.exit(0);
  } catch (err) {
    console.warn(`  ⚠️  Bundled binary incompatible despite platform match: ${err.message}`);
  }
}

console.log(`\n  🔧 Fixing better-sqlite3 binary for ${process.platform}-${process.arch}...`);

// Strategy 1: Copy the correctly-built binary from root node_modules
if (existsSync(rootBinary)) {
  try {
    mkdirSync(dirname(appBinary), { recursive: true });
    copyFileSync(rootBinary, appBinary);
  } catch (err) {
    console.warn(`  ⚠️  Failed to copy binary: ${err.message}`);
  }

  try {
    process.dlopen({ exports: {} }, appBinary);
    console.log("  ✅ Native module fixed successfully!\n");
    process.exit(0);
  } catch (err) {
    console.warn(`  ⚠️  Copied binary failed to load: ${err.message}`);
  }
}

// Strategy 1.5: Use node-pre-gyp to download the correct prebuilt binary
// This works on Windows without requiring node-gyp, Python, or MSVC.
// better-sqlite3 ships prebuilts for win32-x64, win32-arm64, darwin-x64/arm64.
console.log("  📥  Attempting to download prebuilt binary via node-pre-gyp...");
try {
  const { execSync } = await import("node:child_process");
  // better-sqlite3 bundles @mapbox/node-pre-gyp — use it directly
  const preGypBin = join(
    ROOT,
    "app",
    "node_modules",
    ".bin",
    process.platform === "win32" ? "node-pre-gyp.cmd" : "node-pre-gyp"
  );
  const preGypFallback = join(
    ROOT,
    "app",
    "node_modules",
    "@mapbox",
    "node-pre-gyp",
    "bin",
    "node-pre-gyp"
  );
  const preGypCmd = existsSync(preGypBin) ? preGypBin : preGypFallback;

  if (existsSync(preGypCmd)) {
    execSync(`"${process.execPath}" "${preGypCmd}" install --fallback-to-build=false`, {
      cwd: join(ROOT, "app", "node_modules", "better-sqlite3"),
      stdio: "inherit",
      timeout: 60_000,
    });
    mkdirSync(dirname(appBinary), { recursive: true });
    try {
      process.dlopen({ exports: {} }, appBinary);
      console.log("  ✅ Prebuilt binary downloaded and loaded successfully!\n");
      process.exit(0);
    } catch (loadErr) {
      console.warn(`  ⚠️  Downloaded binary failed to load: ${loadErr.message}`);
    }
  } else {
    console.warn("  ⚠️  node-pre-gyp not found, skipping prebuilt download.");
  }
} catch (err) {
  console.warn(`  ⚠️  node-pre-gyp download failed: ${err.message.split("\n")[0]}`);
}

// Strategy 2: Fall back to npm rebuild (may work if build tools are available)
console.log("  ⚠️  Attempting npm rebuild (requires build tools)...");

try {
  const { execSync } = await import("node:child_process");
  execSync("npm rebuild better-sqlite3", {
    cwd: join(ROOT, "app"),
    stdio: "inherit",
    timeout: 120_000,
  });

  process.dlopen({ exports: {} }, appBinary);
  console.log("  ✅ Native module rebuilt successfully!\n");
  process.exit(0);
} catch (err) {
  const isTimeout = err.killed || err.signal === "SIGTERM";
  if (isTimeout) {
    console.warn("  ⚠️  npm rebuild timed out after 120s.");
  } else {
    console.warn(`  ⚠️  npm rebuild failed: ${err.message}`);
  }
}

// If nothing worked, warn but don't fail the install
console.warn("\n  ⚠️  Could not fix better-sqlite3 native module automatically.");
console.warn("     The server may not start correctly.");
console.warn("     Manual fix options:");
if (process.platform === "win32") {
  console.warn("     Option A (easiest — no build tools needed):");
  console.warn(`       cd "${join(ROOT, "app", "node_modules", "better-sqlite3")}"`);
  console.warn("       npx @mapbox/node-pre-gyp install --fallback-to-build=false");
  console.warn("     Option B (requires Build Tools for Visual Studio):");
  console.warn(`       cd "${join(ROOT, "app")}" && npm rebuild better-sqlite3`);
  console.warn("       Install from: https://visualstudio.microsoft.com/visual-cpp-build-tools/");
  console.warn("       Also ensure Python is installed: https://python.org");
} else if (process.platform === "darwin") {
  console.warn(`     cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`);
  console.warn("     If build tools are missing: xcode-select --install");
} else {
  console.warn(`     cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`);
}
console.warn("");

// ── @swc/helpers fix ────────────────────────────────────────────────────────
// Next.js standalone tracer doesn't always include @swc/helpers in app/node_modules/,
// causing a MODULE_NOT_FOUND crash at runtime. Copy it from root node_modules if needed.
const swcHelpersApp = join(ROOT, "app", "node_modules", "@swc", "helpers");
const swcHelpersRoot = join(ROOT, "node_modules", "@swc", "helpers");

if (!existsSync(swcHelpersApp)) {
  if (existsSync(swcHelpersRoot)) {
    try {
      const { cpSync } = await import("node:fs");
      mkdirSync(join(ROOT, "app", "node_modules", "@swc"), { recursive: true });
      cpSync(swcHelpersRoot, swcHelpersApp, { recursive: true });
      console.log("  ✅ @swc/helpers copied to standalone app/node_modules.\n");
    } catch (err) {
      console.warn(`  ⚠️  Could not copy @swc/helpers: ${err.message}`);
      console.warn(
        "     Try manually: cp -r node_modules/@swc/helpers app/node_modules/@swc/helpers\n"
      );
    }
  } else {
    console.warn("  ⚠️  @swc/helpers not found in root node_modules either.");
    console.warn("     Try: npm install --save-exact @swc/helpers@0.5.19\n");
  }
}
