/**
 * Native Module Handler for Desktop Bundling
 * Manages native Node.js modules that can't be bundled
 */
import { ensureDir, pathExists, copy, writeFile, readFile, readdir, stat, } from "../utils/fs-utils.js";
import path from "path";
import { execSync } from "child_process";
import { createLogger } from "../core/logger.js";
const logger = createLogger("native-modules");
/**
 * Default native modules that commonly need special handling
 */
export const COMMON_NATIVE_MODULES = [
    "serialport",
    "@serialport/bindings-cpp",
    "node-pty",
    "bcrypt",
    "better-sqlite3",
    "canvas",
    "sharp",
    "fsevents",
    "usb",
    "node-hid",
];
/**
 * Copy native modules to resources directory
 */
export async function copyNativeModules(config) {
    const { modules, sourceDir, targetDir } = config;
    // Ensure target directory exists
    await ensureDir(path.join(targetDir, "node_modules"));
    for (const moduleName of modules) {
        const sourcePath = path.join(sourceDir, "node_modules", moduleName);
        const targetPath = path.join(targetDir, "node_modules", moduleName);
        if (await pathExists(sourcePath)) {
            logger.info(`📦 Copying native module: ${moduleName}`);
            await copy(sourcePath, targetPath);
        }
        else {
            logger.warn(`⚠️  Native module not found: ${moduleName}`);
        }
    }
}
/**
 * Rebuild native modules for target platform
 */
export async function rebuildNativeModules(targetDir, electronVersion) {
    logger.info("🔨 Rebuilding native modules...");
    const cwd = targetDir;
    if (electronVersion) {
        // Rebuild for Electron
        try {
            execSync(`npx electron-rebuild -v ${electronVersion}`, {
                cwd,
                stdio: "inherit",
            });
        }
        catch (_error) {
            logger.error("Failed to rebuild for Electron:", _error);
            throw _error;
        }
    }
    else {
        // Rebuild for Node.js
        try {
            execSync("npm rebuild", { cwd, stdio: "inherit" });
        }
        catch (_error) {
            logger.error("Failed to rebuild native modules:", _error);
            throw _error;
        }
    }
}
/**
 * Create a loader script that sets up paths for native modules
 */
export async function createNativeModuleLoader(outputPath, nativeModules) {
    const loaderScript = `/**
 * Native Module Loader
 * Sets up require paths for native modules in desktop app
 */

const path = require('path');
const Module = require('module');

// Add native modules directory to Node module paths
const nativeModulesPath = path.join(__dirname, 'node_modules');
Module.globalPaths.push(nativeModulesPath);

// Patch require to handle native modules
const originalRequire = Module.prototype.require;
const nativeModules = ${JSON.stringify(nativeModules)};

Module.prototype.require = function(id) {
  // Check if this is a native module
  if (nativeModules.includes(id) || nativeModules.some(m => id.startsWith(m + '/'))) {
    try {
      // Try to load from native modules directory
      return originalRequire.apply(this, [path.join(nativeModulesPath, id)]);
    } catch (_error) {
      // Fall back to original require
      return originalRequire.apply(this, arguments);
    }
  }
  
  return originalRequire.apply(this, arguments);
};

// Export for use in main bundle
module.exports = { nativeModulesPath };
`;
    await writeFile(outputPath, loaderScript, "utf8");
}
/**
 * Check if a module is a native module
 */
export async function isNativeModule(moduleName) {
    try {
        const packageJsonPath = require.resolve(`${moduleName}/package.json`);
        const content = await readFile(packageJsonPath, "utf8");
        const packageJson = JSON.parse(content);
        // Check for common indicators of native modules
        return !!(packageJson.gypfile ||
            packageJson.binary ||
            packageJson.scripts?.install ||
            packageJson.scripts?.postinstall?.includes("node-gyp") ||
            packageJson.dependencies?.["node-addon-api"] ||
            packageJson.dependencies?.["nan"]);
    }
    catch {
        return false;
    }
}
/**
 * Detect all native modules in a project
 */
export async function detectNativeModules(projectDir) {
    const nodeModulesDir = path.join(projectDir, "node_modules");
    const nativeModules = [];
    if (!(await pathExists(nodeModulesDir))) {
        return nativeModules;
    }
    const modules = await readdir(nodeModulesDir);
    for (const moduleName of modules) {
        if (moduleName.startsWith("."))
            continue;
        const modulePath = path.join(nodeModulesDir, moduleName);
        const stats = await stat(modulePath);
        if (stats.isDirectory()) {
            if (moduleName.startsWith("@")) {
                // Scoped package
                const scopedModules = await readdir(modulePath);
                for (const scopedModule of scopedModules) {
                    const fullName = `${moduleName}/${scopedModule}`;
                    if (await isNativeModule(fullName)) {
                        nativeModules.push(fullName);
                    }
                }
            }
            else {
                // Regular package
                if (await isNativeModule(moduleName)) {
                    nativeModules.push(moduleName);
                }
            }
        }
    }
    return nativeModules;
}
/**
 * Create a bundle that properly handles native modules
 */
export async function bundleWithNativeModules(options) {
    const { entryPoint, outputPath, autoDetect = true, rebuild = false, electronVersion, } = options;
    const projectDir = path.dirname(entryPoint);
    const outputDir = path.dirname(outputPath);
    // Detect or use provided native modules
    let nativeModules = options.nativeModules || [];
    if (autoDetect) {
        const detected = await detectNativeModules(projectDir);
        nativeModules = [...new Set([...nativeModules, ...detected])];
        logger.info(`🔍 Detected native modules: ${nativeModules.join(", ")}`);
    }
    // Copy native modules
    if (nativeModules.length > 0) {
        await copyNativeModules({
            modules: nativeModules,
            sourceDir: projectDir,
            targetDir: outputDir,
        });
        // Create loader script
        await createNativeModuleLoader(path.join(outputDir, "native-loader.js"), nativeModules);
        // Rebuild if requested
        if (rebuild) {
            await rebuildNativeModules(outputDir, electronVersion);
        }
    }
    logger.info("✅ Native modules prepared for bundling");
}
//# sourceMappingURL=native-modules.js.map