"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.installRepositoryAssets = installRepositoryAssets;
exports.purgeRepositoryAssets = purgeRepositoryAssets;
const promises_1 = require("node:fs/promises");
const node_path_1 = __importDefault(require("node:path"));
const command_1 = require("../core/command");
const fs_errors_1 = require("../core/fs-errors");
const file_readers_1 = require("../core/file-readers");
const paths_1 = require("../core/paths");
const REPO_SKILL_TARGETS = [
    {
        worker: "claude",
        directory: paths_1.CLAUDE_REPO_SKILL_DIRECTORY,
    },
    {
        worker: "codex",
        directory: paths_1.CODEX_REPO_SKILL_DIRECTORY,
    },
];
async function installRepositoryAssets(cwd) {
    for (const file of await getInstalledSkillManagedFiles(cwd)) {
        await (0, promises_1.mkdir)(node_path_1.default.dirname(file.targetPath), { recursive: true });
        if (node_path_1.default.resolve(file.canonicalPath) !== node_path_1.default.resolve(file.targetPath)) {
            await (0, promises_1.copyFile)(file.canonicalPath, file.targetPath);
        }
    }
}
async function purgeRepositoryAssets(cwd, options = {}) {
    const managedResult = await purgeManagedRepositoryAssets(cwd, options.force ?? false);
    const localStatePath = await purgeRepositoryLocalState(cwd);
    await removeKnownEmptyDirectories(cwd);
    return {
        removedPaths: [
            ...managedResult.removedPaths,
            ...(localStatePath ? [localStatePath] : []),
        ],
        skippedPaths: managedResult.skippedPaths,
    };
}
async function purgeManagedRepositoryAssets(cwd, force) {
    const removedPaths = [];
    const skippedPaths = [];
    for (const file of await getAllSkillManagedFiles(cwd)) {
        const action = await maybeRemoveManagedFile(file, force);
        if (action === "removed") {
            removedPaths.push(file.targetPath);
        }
        if (action === "skipped") {
            skippedPaths.push(file.targetPath);
        }
    }
    return { removedPaths, skippedPaths };
}
async function purgeRepositoryLocalState(cwd) {
    const localStateDirectory = node_path_1.default.join(cwd, ".alpaca");
    if (!(await (0, file_readers_1.pathExists)(localStateDirectory))) {
        return null;
    }
    await (0, promises_1.rm)(localStateDirectory, { recursive: true, force: true });
    return localStateDirectory;
}
async function getInstalledSkillManagedFiles(cwd) {
    const files = [];
    for (const target of REPO_SKILL_TARGETS) {
        if (await (0, command_1.commandExists)(target.worker)) {
            files.push(...(await getSkillManagedFiles(node_path_1.default.join(cwd, target.directory))));
        }
    }
    return files;
}
async function getAllSkillManagedFiles(cwd) {
    const files = [];
    for (const target of REPO_SKILL_TARGETS) {
        files.push(...(await getSkillManagedFiles(node_path_1.default.join(cwd, target.directory))));
    }
    return files;
}
async function getSkillManagedFiles(targetDirectory) {
    const canonicalDirectory = (0, paths_1.getCanonicalSkillDirectory)();
    const relativeFiles = await listRelativeFiles(canonicalDirectory);
    return relativeFiles.map((relativeFile) => ({
        canonicalPath: node_path_1.default.join(canonicalDirectory, relativeFile),
        targetPath: node_path_1.default.join(targetDirectory, relativeFile),
    }));
}
async function listRelativeFiles(directory) {
    const entries = await (0, promises_1.readdir)(directory, { withFileTypes: true });
    const relativeFiles = [];
    for (const entry of entries) {
        if (entry.isDirectory()) {
            const childFiles = await listRelativeFiles(node_path_1.default.join(directory, entry.name));
            relativeFiles.push(...childFiles.map((childFile) => node_path_1.default.join(entry.name, childFile)));
            continue;
        }
        if (entry.isFile()) {
            relativeFiles.push(entry.name);
        }
    }
    return relativeFiles;
}
async function maybeRemoveManagedFile(file, force) {
    try {
        const actualContents = await (0, promises_1.readFile)(file.targetPath, "utf8");
        if (!force) {
            const canonicalContents = await (0, promises_1.readFile)(file.canonicalPath, "utf8");
            if (actualContents !== canonicalContents) {
                return "skipped";
            }
        }
        await (0, promises_1.rm)(file.targetPath, { force: true });
        return "removed";
    }
    catch (error) {
        if ((0, fs_errors_1.isMissingFileError)(error)) {
            return "missing";
        }
        throw error;
    }
}
async function removeKnownEmptyDirectories(cwd) {
    for (const target of REPO_SKILL_TARGETS) {
        await removeEmptyDirectoriesRecursively(node_path_1.default.join(cwd, target.directory));
    }
    await removeDirectoryIfEmpty(node_path_1.default.join(cwd, ".claude/skills"));
    await removeDirectoryIfEmpty(node_path_1.default.join(cwd, ".claude"));
    await removeDirectoryIfEmpty(node_path_1.default.join(cwd, ".agents/skills"));
    await removeDirectoryIfEmpty(node_path_1.default.join(cwd, ".agents"));
}
async function removeEmptyDirectoriesRecursively(directoryPath) {
    let entries;
    try {
        entries = await (0, promises_1.readdir)(directoryPath, { withFileTypes: true });
    }
    catch (error) {
        if ((0, fs_errors_1.isMissingFileError)(error)) {
            return;
        }
        throw error;
    }
    for (const entry of entries) {
        if (entry.isDirectory()) {
            await removeEmptyDirectoriesRecursively(node_path_1.default.join(directoryPath, entry.name));
        }
    }
    await removeDirectoryIfEmpty(directoryPath);
}
async function removeDirectoryIfEmpty(directoryPath) {
    try {
        await (0, promises_1.rmdir)(directoryPath);
    }
    catch (error) {
        if ((0, fs_errors_1.isAnyErrnoCode)(error, ["ENOENT", "ENOTEMPTY"])) {
            return;
        }
        throw error;
    }
}
