"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.renameAppBundleContents = renameAppBundleContents;
const path_1 = require("path");
const logger_1 = require("../../utils/logger");
const config_service_1 = require("../config/config.service");
const fs_1 = require("fs");
const child_process_1 = require("child_process");
const promises_1 = require("fs/promises");
const logger = logger_1.Logger.getInstance("modifications.macOS.helper");
/**
 * Renames an app bundle contents (not the bundle itself) to a new base name. This means
 * - Updating the executable name inside as well
 * - Updating the Info.plist file accordingly
 * - Updating all Electron Helper apps in Contents/Frameworks to match the new base name (bundle name + executable + plist)
 * - Copies the logo.icns file from Contents/assets/images to Contents/Resources
 *
 * @param appBundlePath Absolute path to YourApp.app
 * @param newBaseName Desired base name (e.g., "JPM Connect")
 *
 */
async function renameAppBundleContents(appBundlePath, newBaseName) {
    logger.debug(`Renaming app bundle contents to a new ${newBaseName}`);
    const contentsPath = (0, path_1.join)(appBundlePath, 'Contents');
    await renameAppBundleExe(contentsPath);
    await updateInfoPlist(contentsPath);
    await renameElectronHelpers(appBundlePath, newBaseName.replace('.app', ''));
    await copyLogoToResources(contentsPath);
}
async function renameAppBundleExe(contentsPath) {
    const currentExePath = (0, path_1.join)(contentsPath, "MacOS/io.Connect Desktop");
    const newExePath = (0, path_1.join)(contentsPath, "MacOS", config_service_1.ConfigManager.config.mac.appBundleName.replace('.app', ''));
    if ((0, fs_1.existsSync)(newExePath)) {
        logger.debug(`Executable already correctly named: ${newExePath}`);
        return;
    }
    logger.debug(`Renaming executable if needed: ${currentExePath} → ${newExePath}`);
    if ((0, fs_1.existsSync)(currentExePath)) {
        (0, child_process_1.execSync)(`mv "${currentExePath}" "${newExePath}"`);
        logger.debug(`Renamed executable: ${currentExePath} → ${newExePath} successfully!`);
    }
    else {
        logger.warn(`Executable not found for renaming: ${currentExePath}`);
    }
}
/**
 * Rename all Helper apps in Contents/Frameworks to match newBaseName.
 * Handles any existing helper app names (e.g., "Electron Helper", "io.Connect Desktop Helper", etc.)
 * Renames executables in MacOS to match the new base name as well.
 * Only renames when the base name differs.
 * Also updates the inner Info.plist files accordingly.
 *
 * @param appBundlePath Absolute path to YourApp.app
 * @param newBaseName   Desired base name (e.g., "JPM Connect")
 */
async function renameElectronHelpers(appBundlePath, newBaseName) {
    try {
        logger.debug(`Renaming Electron Helper apps to match: ${newBaseName}`);
        const frameworksPath = (0, path_1.join)(appBundlePath, 'Contents', 'Frameworks');
        if (!(0, fs_1.existsSync)(frameworksPath)) {
            logger.debug('No Frameworks directory found - skipping Electron Helper renaming');
            return;
        }
        // Find all Helper app bundles in Frameworks (they may have various names)
        const frameworkItems = (0, fs_1.readdirSync)(frameworksPath, { withFileTypes: true });
        const helperApps = frameworkItems
            .filter(item => item.isDirectory() && item.name.includes('Helper') && item.name.endsWith('.app'))
            .map(item => item.name);
        if (helperApps.length === 0) {
            logger.debug('No Helper apps found in Frameworks directory');
            return;
        }
        logger.debug(`Found ${helperApps.length} Helper apps to rename: ${helperApps.join(', ')}`);
        for (const helperAppName of helperApps) {
            const currentHelperPath = (0, path_1.join)(frameworksPath, helperAppName);
            // Determine the new helper app name based on the current helper type
            const newHelperAppName = getNewHelperAppName(helperAppName, newBaseName);
            if (newHelperAppName === helperAppName) {
                logger.debug(`Helper app ${helperAppName} already has correct name - skipping`);
                continue;
            }
            const newHelperPath = (0, path_1.join)(frameworksPath, newHelperAppName);
            try {
                // Rename the helper app bundle
                logger.debug(`Renaming helper app: ${helperAppName} → ${newHelperAppName}`);
                (0, child_process_1.execSync)(`mv "${currentHelperPath}" "${newHelperPath}"`);
                // Update the executable inside MacOS folder
                await renameHelperExecutable(newHelperPath, newBaseName);
                // Update the Info.plist inside the helper app
                await updateHelperInfoPlist(newHelperPath, newBaseName);
                logger.debug(`Successfully renamed helper app: ${helperAppName} → ${newHelperAppName}`);
            }
            catch (error) {
                logger.error(`Failed to rename helper app ${helperAppName}: ${error}`);
                // Continue with other helpers even if one fails
            }
        }
        logger.debug(`Completed renaming Electron Helper apps`);
    }
    catch (error) {
        logger.error(`Failed to rename Electron Helper apps: ${error}`);
        throw error;
    }
}
/**
 * Generate the new helper app name based on the helper type and new base name
 */
function getNewHelperAppName(currentHelperName, newBaseName) {
    // Helper patterns can be:
    // - "Electron Helper.app"
    // - "Electron Helper (GPU).app" 
    // - "io.Connect Desktop Helper.app"
    // - "io.Connect Desktop Helper (GPU).app"
    // - "SomeApp Helper.app"
    // - "SomeApp Helper (Plugin).app"
    // Extract the suffix (e.g., " (GPU)", " (Plugin)", etc.) and the word "Helper"
    const helperMatch = currentHelperName.match(/^(.+?)\s+Helper(\s\([^)]+\))?\.app$/);
    if (helperMatch) {
        const suffix = helperMatch[2] || ''; // The part like " (GPU)" or empty string
        return `${newBaseName} Helper${suffix}.app`;
    }
    // Fallback: if we can't parse the pattern, try to preserve any suffix after "Helper"
    const suffixMatch = currentHelperName.match(/Helper(\s\([^)]+\))?\.app$/);
    const suffix = suffixMatch ? suffixMatch[1] || '' : '';
    return `${newBaseName} Helper${suffix}.app`;
}
/**
 * Rename the executable inside the helper app's MacOS folder
 */
async function renameHelperExecutable(helperAppPath, newBaseName) {
    const macOSPath = (0, path_1.join)(helperAppPath, 'Contents', 'MacOS');
    if (!(0, fs_1.existsSync)(macOSPath)) {
        logger.debug(`No MacOS directory found in helper app: ${helperAppPath}`);
        return;
    }
    const macOSItems = (0, fs_1.readdirSync)(macOSPath);
    const executables = macOSItems.filter(item => {
        const itemPath = (0, path_1.join)(macOSPath, item);
        try {
            const stats = (0, fs_1.statSync)(itemPath);
            return stats.isFile() && (stats.mode & 0o111) !== 0; // Check if executable
        }
        catch {
            return false;
        }
    });
    for (const executable of executables) {
        const currentExePath = (0, path_1.join)(macOSPath, executable);
        // Determine new executable name based on pattern
        const newExeName = getNewHelperExecutableName(executable, newBaseName);
        if (newExeName === executable) {
            logger.debug(`Helper executable ${executable} already has correct name`);
            continue;
        }
        const newExePath = (0, path_1.join)(macOSPath, newExeName);
        try {
            (0, child_process_1.execSync)(`mv "${currentExePath}" "${newExePath}"`);
            logger.debug(`Renamed helper executable: ${executable} → ${newExeName}`);
        }
        catch (error) {
            logger.error(`Failed to rename helper executable ${executable}: ${error}`);
        }
    }
}
/**
 * Generate the new helper executable name
 */
function getNewHelperExecutableName(currentExeName, newBaseName) {
    // Parse the current executable name to extract the suffix
    // Examples:
    // - "Electron Helper"
    // - "Electron Helper (GPU)"
    // - "io.Connect Desktop Helper"
    // - "io.Connect Desktop Helper (Plugin)"
    const helperMatch = currentExeName.match(/^(.+?)\s+Helper(\s\([^)]+\))?$/);
    if (helperMatch) {
        const suffix = helperMatch[2] || ''; // The part like " (GPU)" or empty string
        return `${newBaseName} Helper${suffix}`;
    }
    // Fallback patterns for specific suffixes
    if (currentExeName.includes('Helper (GPU)')) {
        return `${newBaseName} Helper (GPU)`;
    }
    else if (currentExeName.includes('Helper (Plugin)')) {
        return `${newBaseName} Helper (Plugin)`;
    }
    else if (currentExeName.includes('Helper (Renderer)')) {
        return `${newBaseName} Helper (Renderer)`;
    }
    else if (currentExeName.includes('Helper')) {
        return `${newBaseName} Helper`;
    }
    // If no recognized pattern, keep original name
    return currentExeName;
}
async function updateInfoPlist(contentsPath) {
    logger.debug(`Patching Info.plist for iocd component on macOS`);
    const plistPath = (0, path_1.join)(contentsPath, "Info.plist");
    const bundleNameWithoutApp = config_service_1.ConfigManager.config.mac.appBundleName.replace('.app', '');
    const plist = require("plist");
    const info = plist.parse((0, fs_1.readFileSync)(plistPath, "utf8"));
    // replace any special characters with dots and lowercase the name from package.json
    info.CFBundleIdentifier = config_service_1.ConfigManager.config.mac.appBundleId;
    // this needs to match the name of the app bundle, so we leave it unchanged; some day we might rename the app bundle to match the product name
    info.CFBundleName = bundleNameWithoutApp;
    info.CFBundleExecutable = bundleNameWithoutApp;
    info.CFBundleDisplayName = bundleNameWithoutApp;
    info.CFBundleVersion = config_service_1.ConfigManager.config.version;
    info.CFBundleShortVersionString = config_service_1.ConfigManager.config.version;
    info.CFBundleIconFile = "logo.icns";
    (0, fs_1.writeFileSync)(plistPath, plist.build(info), "utf8");
    logger.debug(`Patched Info.plist successfully!`);
}
/**
 * Update the Info.plist file inside the helper app
 */
async function updateHelperInfoPlist(helperAppPath, newBaseName) {
    const infoPlistPath = (0, path_1.join)(helperAppPath, 'Contents', 'Info.plist');
    const plist = require("plist");
    logger.debug(`Patching ${infoPlistPath} for helper app`);
    if (!(0, fs_1.existsSync)(infoPlistPath)) {
        logger.debug(`No Info.plist found in helper app: ${helperAppPath}`);
        return;
    }
    try {
        const plistContent = (0, fs_1.readFileSync)(infoPlistPath, 'utf8');
        const plistData = plist.parse(plistContent);
        // Update relevant fields
        if (plistData.CFBundleName) {
            const newBundleName = getNewHelperExecutableName(plistData.CFBundleName, newBaseName);
            plistData.CFBundleName = newBundleName;
        }
        if (plistData.CFBundleDisplayName) {
            const newDisplayName = getNewHelperExecutableName(plistData.CFBundleDisplayName, newBaseName);
            plistData.CFBundleDisplayName = newDisplayName;
        }
        if (plistData.CFBundleExecutable) {
            const newExecutableName = getNewHelperExecutableName(plistData.CFBundleExecutable, newBaseName);
            plistData.CFBundleExecutable = newExecutableName;
        }
        // Update bundle identifier to reflect new name
        if (plistData.CFBundleIdentifier) {
            const mainIdentifier = config_service_1.ConfigManager.config.mac.appBundleId;
            // If the main bundle identifier is "com.interopio.io.connect.desktop", 
            // then helper might be "com.interopio.io.connect.desktop.helper"
            // or "com.interopio.io.connect.desktop.helper.GPU", etc.
            // get suffix from the string in () in the new bundle name
            const suffixMatch = plistData.CFBundleName.indexOf('(') !== -1 ? plistData.CFBundleName.match(/\(([^)]+)\)/) : null;
            const suffix = suffixMatch ? `helper.${suffixMatch[1]}` : 'helper';
            const identifier = `${mainIdentifier}.${suffix}`;
            // Replace any old app name in bundle identifier with new name
            plistData.CFBundleIdentifier = identifier;
        }
        // Write updated plist
        const updatedPlistContent = plist.build(plistData);
        (0, fs_1.writeFileSync)(infoPlistPath, updatedPlistContent, 'utf8');
        logger.debug(`Patched Info.plist for helper app: ${helperAppPath}`);
    }
    catch (error) {
        logger.error(`Failed to update helper Info.plist at ${infoPlistPath}: ${error}`);
    }
}
/** Copies the logo.icns file from Contents/assets/images to Contents/Resources */
async function copyLogoToResources(contentsPath) {
    const sourcePath = (0, path_1.join)(contentsPath, "assets/images/logo.icns");
    const destPath = (0, path_1.join)(contentsPath, "Resources/logo.icns");
    if (!(0, fs_1.existsSync)(sourcePath)) {
        logger.warn(`Source logo.icns not found: ${sourcePath}`);
        return;
    }
    await (0, promises_1.copyFile)(sourcePath, destPath);
    logger.debug(`Copied logo.icns to Resources: ${destPath}`);
}
//# sourceMappingURL=macOS.helper.js.map