/**
 * Hooks writer — copies trusted hook sources into `{agentDir}/hooks/`.
 * Caller filters by `supports(agent, 'hooks', version)` before invoking.
 * Orphan sweep (delete hooks in version-home that aren't in `availableNames`)
 * stays in the orchestrator since it depends on the broader available set.
 */
import * as fs from 'fs';
import { agentConfigDirName } from '../../agents.js';
import * as path from 'path';
import { capableAgents } from '../../capabilities.js';
import { safeJoin } from '../../paths.js';
import { registerHooksToSettings } from '../../hooks/install.js';
import { resolveHookSource } from './sources.js';
import { lazyAgentMap } from './lazy-map.js';
function removePath(target) {
    try {
        const stat = fs.lstatSync(target);
        if (stat.isSymbolicLink() || stat.isFile()) {
            fs.unlinkSync(target);
        }
        else if (stat.isDirectory()) {
            fs.rmSync(target, { recursive: true, force: true });
        }
    }
    catch {
        /* already gone */
    }
}
function copyDir(src, dest) {
    fs.mkdirSync(dest, { recursive: true });
    const entries = fs.readdirSync(src, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.isSymbolicLink())
            continue;
        const srcPath = safeJoin(src, entry.name);
        const destPath = safeJoin(dest, entry.name);
        if (entry.isDirectory()) {
            copyDir(srcPath, destPath);
        }
        else if (entry.isFile()) {
            fs.copyFileSync(srcPath, destPath);
            fs.chmodSync(destPath, fs.statSync(srcPath).mode);
        }
    }
}
function copyHookSource(src, dest) {
    let stat;
    try {
        stat = fs.lstatSync(src);
    }
    catch {
        return false;
    }
    if (stat.isSymbolicLink())
        return false;
    removePath(dest);
    if (stat.isDirectory()) {
        copyDir(src, dest);
        return true;
    }
    if (stat.isFile()) {
        fs.mkdirSync(path.dirname(dest), { recursive: true });
        fs.copyFileSync(src, dest);
        fs.chmodSync(dest, 0o755);
        return true;
    }
    return false;
}
function buildHooksWriter(agent) {
    return {
        kind: 'hooks',
        agent,
        write({ versionHome, selection }) {
            const agentDir = path.join(versionHome, agentConfigDirName(agent));
            const hooksTarget = path.join(agentDir, 'hooks');
            fs.mkdirSync(hooksTarget, { recursive: true });
            const synced = [];
            const paths = [];
            for (const hook of selection) {
                const srcFile = resolveHookSource(hook);
                if (!srcFile)
                    continue;
                const destFile = safeJoin(hooksTarget, hook);
                if (copyHookSource(srcFile, destFile)) {
                    synced.push(hook);
                    paths.push(destFile);
                }
            }
            // Native hook registration in settings.json/hooks.json. Grok is included
            // so subrule-bundled guards (absolute paths outside the central hooks/
            // copy set) get registered into ~/.grok/hooks/hooks.json via
            // registerHooksForGrok — file copy alone only sees top-level available.hooks
            // names (RUSH-1353). Copilot/Goose load managed *.json under their
            // hooks dirs the same way.
            if (agent === 'claude' || agent === 'codex' || agent === 'antigravity' || agent === 'kimi' || agent === 'droid' || agent === 'copilot' || agent === 'goose' || agent === 'cursor' || agent === 'grok' || agent === 'hermes' || agent === 'muse') {
                registerHooksToSettings(agent, versionHome);
            }
            return { synced, paths };
        },
        remove({ versionHome, name }) {
            // Delete the copied hook artifact from `{agentDir}/hooks/<name>`. Native
            // settings.json/hooks.json registration is regenerated by the write path
            // that runs alongside this prune (registerHooksToSettings above reads the
            // remaining source manifest), so a removed hook drops out of both.
            const agentDir = path.join(versionHome, agentConfigDirName(agent));
            const hookTarget = safeJoin(path.join(agentDir, 'hooks'), name);
            try {
                if (fs.existsSync(hookTarget)) {
                    removePath(hookTarget);
                    return { removed: true };
                }
            }
            catch { /* already gone / inaccessible */ }
            return { removed: false };
        },
    };
}
export const hooksWriters = lazyAgentMap(() => {
    const m = {};
    for (const agent of capableAgents('hooks'))
        m[agent] = buildHooksWriter(agent);
    return m;
});
