import chalk from 'chalk';
import { createHash } from 'node:crypto';
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
import { homedir } from 'node:os';
import { basename, dirname, join, resolve } from 'node:path';
import { createInterface } from 'node:readline/promises';
import { fileURLToPath } from 'node:url';
const PLUGIN_NAME = 'shortcutxl';
const IGNORED_ENTRIES = new Set(['.git', 'node_modules', '.DS_Store', '.in_use', '.orphaned_at']);
const REQUIRED_PLUGIN_FILES = [
    'SKILL.md',
    `skills/${PLUGIN_NAME}/SKILL.md`,
    '.claude-plugin/plugin.json',
    '.codex-plugin/plugin.json'
];
function hasInstalledShortcutXLPluginShape(path) {
    if (!existsSync(path))
        return false;
    return (existsSync(join(path, 'SKILL.md')) ||
        existsSync(join(path, 'skills', PLUGIN_NAME, 'SKILL.md')) ||
        existsSync(join(path, '.claude-plugin', 'plugin.json')) ||
        existsSync(join(path, '.codex-plugin', 'plugin.json')));
}
function hasBundledShortcutXLPluginShape(path) {
    return REQUIRED_PLUGIN_FILES.every((relativePath) => existsSync(join(path, relativePath)));
}
function getRuntimeRelativePluginCandidates() {
    const here = dirname(fileURLToPath(import.meta.url));
    return [
        join(here, '..', 'plugins', PLUGIN_NAME),
        join(here, '..', '..', 'plugins', PLUGIN_NAME),
        join(here, '..', '..', '..', 'plugins', PLUGIN_NAME),
        join(here, '..', '..', '..', '..', 'plugins', PLUGIN_NAME)
    ].map((path) => resolve(path));
}
export function resolveBundledShortcutXLPluginRoot(sourceRoot) {
    const candidates = [
        sourceRoot,
        process.env.SHORTCUTXL_PLUGIN_SOURCE_DIR,
        ...getRuntimeRelativePluginCandidates()
    ].filter((path) => Boolean(path));
    for (const candidate of candidates) {
        const resolved = resolve(candidate);
        if (hasBundledShortcutXLPluginShape(resolved)) {
            return resolved;
        }
    }
    return undefined;
}
function collectClaudePluginInstalls(homeDir) {
    const installs = [];
    // Sync only durable plugin installs. Claude cache directories are host-managed derived state.
    const marketplaceRoot = join(homeDir, '.claude', 'plugins', 'marketplaces', 'shortcutxl-plugins', PLUGIN_NAME);
    if (hasInstalledShortcutXLPluginShape(marketplaceRoot)) {
        installs.push({ provider: 'claude', rootPath: marketplaceRoot });
    }
    return installs;
}
function collectCodexPluginInstalls(homeDir) {
    const installs = [];
    // Do not sync ~/.codex/.tmp/plugins; Codex owns that cache.
    const skillRoot = join(homeDir, '.codex', 'skills', PLUGIN_NAME);
    if (hasInstalledShortcutXLPluginShape(skillRoot)) {
        installs.push({ provider: 'codex', rootPath: skillRoot });
    }
    return installs;
}
export function discoverShortcutXLPluginInstalls(homeDir = homedir()) {
    const installs = [
        ...collectClaudePluginInstalls(homeDir),
        ...collectCodexPluginInstalls(homeDir)
    ];
    const seen = new Set();
    return installs.filter((install) => {
        const key = resolve(install.rootPath);
        if (seen.has(key))
            return false;
        seen.add(key);
        return true;
    });
}
function listRelativeFiles(root) {
    const files = [];
    const visit = (dir, prefix = '') => {
        for (const entry of readdirSync(dir, { withFileTypes: true })) {
            if (IGNORED_ENTRIES.has(entry.name))
                continue;
            const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
            const fullPath = join(dir, entry.name);
            if (entry.isDirectory()) {
                visit(fullPath, rel);
            }
            else if (entry.isFile()) {
                files.push(rel);
            }
        }
    };
    visit(root);
    return files.sort((a, b) => a.localeCompare(b));
}
function fileHash(path) {
    return createHash('sha256').update(readFileSync(path)).digest('hex');
}
export function shortcutXLPluginRootsDiverge(sourceRoot, installedRoot) {
    const sourceFiles = listRelativeFiles(sourceRoot);
    const installedFiles = listRelativeFiles(installedRoot);
    if (sourceFiles.length !== installedFiles.length)
        return true;
    for (let index = 0; index < sourceFiles.length; index += 1) {
        const rel = sourceFiles[index];
        if (installedFiles[index] !== rel)
            return true;
        if (fileHash(join(sourceRoot, rel)) !== fileHash(join(installedRoot, rel)))
            return true;
    }
    return false;
}
function copyPluginRoot(sourceRoot, installedRoot) {
    const parent = dirname(installedRoot);
    mkdirSync(parent, { recursive: true });
    mkdirSync(installedRoot, { recursive: true });
    for (const entry of readdirSync(installedRoot, { withFileTypes: true })) {
        if (IGNORED_ENTRIES.has(entry.name))
            continue;
        rmSync(join(installedRoot, entry.name), { recursive: true, force: true });
    }
    cpSync(sourceRoot, installedRoot, {
        recursive: true,
        filter: (src) => !IGNORED_ENTRIES.has(basename(src))
    });
}
async function promptConfirmInteractive(install) {
    const rl = createInterface({ input: process.stdin, output: process.stdout });
    try {
        const answer = await rl.question(`Update installed ShortcutXL ${install.provider} plugin at ${install.rootPath}? [y/N] `);
        return answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes';
    }
    finally {
        rl.close();
    }
}
export async function syncInstalledShortcutXLPlugins(options = {}) {
    const sourceRoot = resolveBundledShortcutXLPluginRoot(options.sourceRoot);
    const installs = discoverShortcutXLPluginInstalls(options.homeDir);
    const result = {
        sourceRoot,
        checked: installs,
        updated: [],
        skipped: []
    };
    if (!sourceRoot || installs.length === 0) {
        return result;
    }
    const write = options.write ?? ((text) => process.stdout.write(text));
    const canPrompt = options.canPrompt ?? (process.stdin.isTTY && process.stdout.isTTY);
    const confirm = options.promptConfirm ?? promptConfirmInteractive;
    for (const install of installs) {
        if (!shortcutXLPluginRootsDiverge(sourceRoot, install.rootPath)) {
            continue;
        }
        if (!canPrompt) {
            result.skipped.push(install);
            continue;
        }
        write([
            '',
            chalk.yellow(`ShortcutXL ${install.provider} plugin differs from the bundled version.`),
            chalk.dim(`  Installed: ${install.rootPath}`),
            chalk.dim(`  Bundled:   ${sourceRoot}`),
            chalk.dim('  Approval replaces the installed plugin folder with the bundled copy.'),
            chalk.dim('  Local edits in that plugin folder will be overwritten.'),
            ''
        ].join('\n'));
        if (await confirm(install)) {
            copyPluginRoot(sourceRoot, install.rootPath);
            result.updated.push(install);
            write(chalk.green(`Updated ShortcutXL ${install.provider} plugin.\n`));
        }
        else {
            result.skipped.push(install);
        }
    }
    return result;
}
//# sourceMappingURL=shortcutxl-plugin-sync.js.map