"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.revertAgentConfiguration = revertAgentConfiguration;
exports.cleanUpAuxiliaryFiles = cleanUpAuxiliaryFiles;
const path = __importStar(require("path"));
const fs_1 = require("fs");
const agent_utils_1 = require("../agents/agent-utils");
const mcp_1 = require("../paths/mcp");
const constants_1 = require("../constants");
const settings_1 = require("../vscode/settings");
/**
 * Checks if a file exists.
 */
async function fileExists(filePath) {
    try {
        await fs_1.promises.access(filePath);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Restores a file from its backup if the backup exists.
 */
async function restoreFromBackup(filePath, verbose, dryRun) {
    const backupPath = `${filePath}.bak`;
    const backupExists = await fileExists(backupPath);
    if (!backupExists) {
        (0, constants_1.logVerbose)(`No backup found for: ${filePath}`, verbose);
        return false;
    }
    const prefix = (0, constants_1.actionPrefix)(dryRun);
    if (dryRun) {
        (0, constants_1.logVerbose)(`${prefix} Would restore: ${filePath} from backup`, verbose);
    }
    else {
        await fs_1.promises.copyFile(backupPath, filePath);
        (0, constants_1.logVerbose)(`${prefix} Restored: ${filePath} from backup`, verbose);
    }
    return true;
}
/**
 * Removes a file if it exists and has no backup (meaning it was generated by ruler).
 */
async function removeGeneratedFile(filePath, verbose, dryRun) {
    const fileExistsFlag = await fileExists(filePath);
    const backupExists = await fileExists(`${filePath}.bak`);
    if (!fileExistsFlag) {
        (0, constants_1.logVerbose)(`File does not exist: ${filePath}`, verbose);
        return false;
    }
    if (backupExists) {
        (0, constants_1.logVerbose)(`File has backup, skipping removal: ${filePath}`, verbose);
        return false;
    }
    const prefix = (0, constants_1.actionPrefix)(dryRun);
    if (dryRun) {
        (0, constants_1.logVerbose)(`${prefix} Would remove generated file: ${filePath}`, verbose);
    }
    else {
        await fs_1.promises.unlink(filePath);
        (0, constants_1.logVerbose)(`${prefix} Removed generated file: ${filePath}`, verbose);
    }
    return true;
}
/**
 * Removes backup files.
 */
async function removeBackupFile(filePath, verbose, dryRun) {
    const backupPath = `${filePath}.bak`;
    const backupExists = await fileExists(backupPath);
    if (!backupExists) {
        return false;
    }
    const prefix = (0, constants_1.actionPrefix)(dryRun);
    if (dryRun) {
        (0, constants_1.logVerbose)(`${prefix} Would remove backup file: ${backupPath}`, verbose);
    }
    else {
        await fs_1.promises.unlink(backupPath);
        (0, constants_1.logVerbose)(`${prefix} Removed backup file: ${backupPath}`, verbose);
    }
    return true;
}
/**
 * Recursively checks if a directory contains only empty directories
 */
async function isDirectoryTreeEmpty(dirPath) {
    try {
        const entries = await fs_1.promises.readdir(dirPath);
        if (entries.length === 0) {
            return true;
        }
        for (const entry of entries) {
            const entryPath = path.join(dirPath, entry);
            const entryStat = await fs_1.promises.stat(entryPath);
            if (entryStat.isFile()) {
                return false;
            }
            else if (entryStat.isDirectory()) {
                const isEmpty = await isDirectoryTreeEmpty(entryPath);
                if (!isEmpty) {
                    return false;
                }
            }
        }
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Helper function to execute directory removal with consistent dry-run handling and logging.
 */
async function executeDirectoryAction(dirPath, action, verbose, dryRun) {
    const prefix = (0, constants_1.actionPrefix)(dryRun);
    const actionText = action === 'remove-tree' ? 'directory tree' : 'directory';
    if (dryRun) {
        (0, constants_1.logVerbose)(`${prefix} Would remove empty ${actionText}: ${dirPath}`, verbose);
    }
    else {
        await fs_1.promises.rm(dirPath, { recursive: true });
        (0, constants_1.logVerbose)(`${prefix} Removed empty ${actionText}: ${dirPath}`, verbose);
    }
    return true;
}
/**
 * Attempts to remove a single empty directory if it exists and is empty.
 */
async function removeEmptyDirectory(dirPath, verbose, dryRun, logMissing = false) {
    try {
        const stat = await fs_1.promises.stat(dirPath);
        if (!stat.isDirectory()) {
            return false;
        }
        const isEmpty = await isDirectoryTreeEmpty(dirPath);
        if (isEmpty) {
            return await executeDirectoryAction(dirPath, 'remove-tree', verbose, dryRun);
        }
        return false;
    }
    catch {
        if (logMissing) {
            (0, constants_1.logVerbose)(`Directory ${dirPath} doesn't exist or can't be accessed`, verbose);
        }
        return false;
    }
}
/**
 * Handles special cleanup logic for .augment directory and its rules subdirectory.
 */
async function removeAugmentDirectory(projectRoot, verbose, dryRun) {
    const augmentDir = path.join(projectRoot, '.augment');
    let directoriesRemoved = 0;
    try {
        const augmentStat = await fs_1.promises.stat(augmentDir);
        if (!augmentStat.isDirectory()) {
            return 0;
        }
        const rulesDir = path.join(augmentDir, 'rules');
        const rulesRemoved = await removeEmptyDirectory(rulesDir, verbose, dryRun);
        if (rulesRemoved) {
            directoriesRemoved++;
        }
        const augmentRemoved = await removeEmptyDirectory(augmentDir, verbose, dryRun);
        if (augmentRemoved) {
            directoriesRemoved++;
        }
    }
    catch {
        // .augment directory doesn't exist, that's fine. leaving comment as catch block can't be kept empty.
    }
    return directoriesRemoved;
}
/**
 * Removes empty directories that were created by ruler.
 * Only removes directories if they are empty and were likely created by ruler.
 * Special handling for .augment directory to clean up rules subdirectory.
 */
async function removeEmptyDirectories(projectRoot, verbose, dryRun) {
    const rulerCreatedDirs = [
        '.github',
        '.cursor',
        '.windsurf',
        '.junie',
        '.openhands',
        '.idx',
        '.gemini',
        '.vscode',
        '.augmentcode',
        '.kilocode',
    ];
    let directoriesRemoved = 0;
    // Handle .augment directory with special logic
    directoriesRemoved += await removeAugmentDirectory(projectRoot, verbose, dryRun);
    // Handle all other ruler-created directories
    for (const dirName of rulerCreatedDirs) {
        const dirPath = path.join(projectRoot, dirName);
        const removed = await removeEmptyDirectory(dirPath, verbose, dryRun, true);
        if (removed) {
            directoriesRemoved++;
        }
    }
    return directoriesRemoved;
}
/**
 * Removes additional files created by specific agents that aren't covered by their main output paths.
 */
async function removeAdditionalAgentFiles(projectRoot, verbose, dryRun) {
    const additionalFiles = [
        '.gemini/settings.json',
        '.mcp.json',
        '.vscode/mcp.json',
        '.cursor/mcp.json',
        '.junie/mcp/mcp.json',
        '.kilocode/mcp.json',
        'config.toml',
    ];
    let filesRemoved = 0;
    const prefix = (0, constants_1.actionPrefix)(dryRun);
    for (const filePath of additionalFiles) {
        const fullPath = path.join(projectRoot, filePath);
        try {
            const fileExistsFlag = await fileExists(fullPath);
            if (!fileExistsFlag) {
                continue;
            }
            const backupExists = await fileExists(`${fullPath}.bak`);
            if (backupExists) {
                const restored = await restoreFromBackup(fullPath, verbose, dryRun);
                if (restored) {
                    filesRemoved++;
                }
            }
            else {
                if (dryRun) {
                    (0, constants_1.logVerbose)(`${prefix} Would remove additional file: ${fullPath}`, verbose);
                }
                else {
                    await fs_1.promises.unlink(fullPath);
                    (0, constants_1.logVerbose)(`${prefix} Removed additional file: ${fullPath}`, verbose);
                }
                filesRemoved++;
            }
        }
        catch {
            (0, constants_1.logVerbose)(`Additional file ${fullPath} doesn't exist or can't be accessed`, verbose);
        }
    }
    const settingsPath = (0, settings_1.getVSCodeSettingsPath)(projectRoot);
    const backupPath = `${settingsPath}.bak`;
    if (await fileExists(backupPath)) {
        const restored = await restoreFromBackup(settingsPath, verbose, dryRun);
        if (restored) {
            filesRemoved++;
            (0, constants_1.logVerbose)(`${constants_1.actionPrefix} Restored VSCode settings from backup`, verbose);
        }
    }
    else if (await fileExists(settingsPath)) {
        try {
            if (dryRun) {
                const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
                if (settings['augment.advanced']) {
                    delete settings['augment.advanced'];
                    const remainingKeys = Object.keys(settings);
                    if (remainingKeys.length === 0) {
                        (0, constants_1.logVerbose)(`${constants_1.actionPrefix} Would remove empty VSCode settings file`, verbose);
                    }
                    else {
                        (0, constants_1.logVerbose)(`${constants_1.actionPrefix} Would remove augment.advanced section from ${settingsPath}`, verbose);
                    }
                    filesRemoved++;
                }
            }
            else {
                const settings = await (0, settings_1.readVSCodeSettings)(settingsPath);
                if (settings['augment.advanced']) {
                    delete settings['augment.advanced'];
                    const remainingKeys = Object.keys(settings);
                    if (remainingKeys.length === 0) {
                        await fs_1.promises.unlink(settingsPath);
                        (0, constants_1.logVerbose)(`${constants_1.actionPrefix} Removed empty VSCode settings file`, verbose);
                    }
                    else {
                        await (0, settings_1.writeVSCodeSettings)(settingsPath, settings);
                        (0, constants_1.logVerbose)(`${constants_1.actionPrefix} Removed augment.advanced section from VSCode settings`, verbose);
                    }
                    filesRemoved++;
                }
                else {
                    (0, constants_1.logVerbose)(`No augment.advanced section found in ${settingsPath}`, verbose);
                }
            }
        }
        catch (error) {
            (0, constants_1.logVerbose)(`Failed to process VSCode settings.json: ${error}`, verbose);
        }
    }
    return filesRemoved;
}
/**
 * Reverts configuration for a single agent.
 * @param agent The agent to revert
 * @param projectRoot Root directory of the project
 * @param agentConfig Agent-specific configuration
 * @param keepBackups Whether to keep backup files
 * @param verbose Whether to enable verbose logging
 * @param dryRun Whether to perform a dry run
 * @returns Promise resolving to revert statistics
 */
async function revertAgentConfiguration(agent, projectRoot, agentConfig, keepBackups, verbose, dryRun) {
    const result = {
        restored: 0,
        removed: 0,
        backupsRemoved: 0,
    };
    const outputPaths = (0, agent_utils_1.getAgentOutputPaths)(agent, projectRoot, agentConfig);
    (0, constants_1.logVerbose)(`Agent ${agent.getName()} output paths: ${outputPaths.join(', ')}`, verbose);
    for (const outputPath of outputPaths) {
        const restored = await restoreFromBackup(outputPath, verbose, dryRun);
        if (restored) {
            result.restored++;
            if (!keepBackups) {
                const backupRemoved = await removeBackupFile(outputPath, verbose, dryRun);
                if (backupRemoved) {
                    result.backupsRemoved++;
                }
            }
        }
        else {
            const removed = await removeGeneratedFile(outputPath, verbose, dryRun);
            if (removed) {
                result.removed++;
            }
        }
    }
    // Handle MCP files
    const mcpPath = await (0, mcp_1.getNativeMcpPath)(agent.getName(), projectRoot);
    if (mcpPath && mcpPath.startsWith(projectRoot)) {
        if (agent.getName() === 'AugmentCode' &&
            mcpPath.endsWith('.vscode/settings.json')) {
            (0, constants_1.logVerbose)(`Skipping MCP handling for AugmentCode settings.json - handled separately`, verbose);
        }
        else {
            const mcpRestored = await restoreFromBackup(mcpPath, verbose, dryRun);
            if (mcpRestored) {
                result.restored++;
                if (!keepBackups) {
                    const mcpBackupRemoved = await removeBackupFile(mcpPath, verbose, dryRun);
                    if (mcpBackupRemoved) {
                        result.backupsRemoved++;
                    }
                }
            }
            else {
                const mcpRemoved = await removeGeneratedFile(mcpPath, verbose, dryRun);
                if (mcpRemoved) {
                    result.removed++;
                }
            }
        }
    }
    return result;
}
/**
 * Cleans up auxiliary files and directories.
 * @param projectRoot Root directory of the project
 * @param verbose Whether to enable verbose logging
 * @param dryRun Whether to perform a dry run
 * @returns Promise resolving to cleanup statistics
 */
async function cleanUpAuxiliaryFiles(projectRoot, verbose, dryRun) {
    const additionalFilesRemoved = await removeAdditionalAgentFiles(projectRoot, verbose, dryRun);
    const directoriesRemoved = await removeEmptyDirectories(projectRoot, verbose, dryRun);
    return {
        additionalFilesRemoved,
        directoriesRemoved,
    };
}
