/**
 * `olam flywheel install-sessionstart-hook [--scope project|user] [--uninstall]`
 *
 * Wires `olam flywheel session-start` into `<scope>/.claude/settings.json`
 * hooks.SessionStart so Claude Code injects operator-state context (active
 * workspace + active world + most-recent wake-brief) before the first user
 * prompt. The 4th defense layer in Olam's hook chain (FF / PreToolUse /
 * PostToolUse / Stop).
 *
 * Coexists with the existing skills-sync SessionStart hook
 * (sentinel `olam-skills-sync-hook-v1`). Both entries sit side-by-side in
 * the SessionStart array; Claude Code runs each matcher in order and merges
 * their `additionalContext` outputs into the first turn's system prompt.
 *
 * Idempotent: re-running over an existing install is a no-op (sentinel match
 * via `mergeHomeSettingsJson`).
 *
 * Pairs with: `docs/runbooks/install-sessionstart-hook.md`,
 *             `docs/decisions/051-sessionstart-hook-4th-defense.md`.
 */
import { existsSync, copyFileSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
import { mergeHomeSettingsJson } from '@olam/core/src/world/merge-settings.js';
import { printError, printInfo, printSuccess, printWarning } from '../../output.js';
import { SESSIONSTART_HOOK_SENTINEL } from './session-start.js';
export const SESSIONSTART_HOOK_STAGE = 'SessionStart';
export const SESSIONSTART_HOOK_TIMEOUT_MS = 5000;
const NOOP_GUARD = 'command -v olam >/dev/null 2>&1 || exit 0;';
/**
 * Build the matcher-entry object that lands under hooks.SessionStart in
 * .claude/settings.json. Embeds the sentinel as a leading no-op shell
 * assignment so `command.includes(sentinel)` works for idempotent install +
 * surgical uninstall, then guards against uninstalled-olam to keep the hook
 * silent when the CLI is missing (audited by `audit:hook-guards`).
 */
export function buildSessionStartHookEntry() {
    return {
        matcher: '',
        hooks: [
            {
                type: 'command',
                command: `OLAM_SESSIONSTART_SENTINEL=${SESSIONSTART_HOOK_SENTINEL}; ${NOOP_GUARD} olam flywheel session-start`,
                timeout: SESSIONSTART_HOOK_TIMEOUT_MS,
            },
        ],
    };
}
function settingsPathFor(scope, cwd) {
    if (scope === 'user')
        return join(homedir(), '.claude', 'settings.json');
    return join(cwd ?? process.cwd(), '.claude', 'settings.json');
}
function backup(filePath) {
    if (!existsSync(filePath))
        return null;
    const ts = new Date().toISOString().replace(/[:.]/g, '-');
    const backupPath = `${filePath}.olam-bak.${ts}`;
    copyFileSync(filePath, backupPath);
    return backupPath;
}
/**
 * Install (idempotent) — programmatic API for both the CLI action and tests.
 * Creates parent dir if needed; backs up an existing settings.json before
 * writing (auto-deletes the backup when the install is a no-op).
 */
export function installSessionStartHook(filePath) {
    mkdirSync(dirname(filePath), { recursive: true });
    const backupPath = backup(filePath);
    const result = mergeHomeSettingsJson(filePath, {
        ensureHook: {
            stage: SESSIONSTART_HOOK_STAGE,
            sentinel: SESSIONSTART_HOOK_SENTINEL,
            entry: buildSessionStartHookEntry(),
        },
    });
    if (result.status === 'already-present' && backupPath) {
        try {
            unlinkSync(backupPath);
        }
        catch {
            // best-effort
        }
        return { status: 'already-present', filePath, backupPath: null };
    }
    return { status: result.status, filePath, backupPath };
}
/**
 * Uninstall (surgical) — walks hooks.SessionStart, drops inner entries whose
 * command contains the sentinel, drops the outer matcher when its hooks
 * array becomes empty. Other SessionStart entries (e.g. the skills-sync
 * hook) are preserved verbatim.
 */
export function uninstallSessionStartHook(filePath) {
    if (!existsSync(filePath))
        return { status: 'no-settings', filePath };
    const raw = readFileSync(filePath, 'utf-8');
    const settings = raw.trim() ? JSON.parse(raw) : {};
    const matchers = settings.hooks?.SessionStart;
    if (!Array.isArray(matchers) || matchers.length === 0) {
        return { status: 'not-found', filePath };
    }
    let changed = false;
    const kept = [];
    for (const matcher of matchers) {
        const inner = matcher.hooks ?? [];
        const keptInner = inner.filter((h) => {
            if (typeof h.command === 'string' && h.command.includes(SESSIONSTART_HOOK_SENTINEL)) {
                changed = true;
                return false;
            }
            return true;
        });
        if (keptInner.length === 0 && inner.length > 0) {
            changed = true;
            continue;
        }
        if (keptInner.length === inner.length) {
            kept.push(matcher);
        }
        else {
            kept.push({ ...matcher, hooks: keptInner });
        }
    }
    if (!changed)
        return { status: 'not-found', filePath };
    const next = {
        ...settings,
        hooks: { ...settings.hooks, SessionStart: kept },
    };
    if (kept.length === 0) {
        const otherStages = Object.keys(next.hooks ?? {}).filter((k) => k !== 'SessionStart');
        if (otherStages.length === 0) {
            delete next.hooks;
        }
        else {
            delete next.hooks.SessionStart;
        }
    }
    writeFileSync(filePath, JSON.stringify(next, null, 2) + '\n');
    return { status: 'removed', filePath };
}
export function registerFlywheelInstallSessionStartHook(parent) {
    parent
        .command('install-sessionstart-hook')
        .description('Install (or --uninstall) the SessionStart context hook into .claude/settings.json. Idempotent. 4th defense layer — preloads operator state before the first prompt.')
        .option('--scope <scope>', 'project (default; <cwd>/.claude/settings.json) or user (~/.claude/settings.json)', 'project')
        .option('--uninstall', 'Remove the hook instead of installing it')
        .action((opts) => {
        const scope = opts.scope === 'user' ? 'user' : 'project';
        const filePath = settingsPathFor(scope);
        if (opts.uninstall) {
            try {
                const r = uninstallSessionStartHook(filePath);
                switch (r.status) {
                    case 'removed':
                        printSuccess(`SessionStart context hook removed from ${filePath}`);
                        return;
                    case 'not-found':
                        printInfo('sessionstart hook', `not found in ${filePath} — already uninstalled`);
                        return;
                    case 'no-settings':
                        printInfo('sessionstart hook', `no settings.json at ${filePath} — nothing to remove`);
                        return;
                }
            }
            catch (err) {
                printError(`uninstall failed: ${err instanceof Error ? err.message : String(err)}`);
                process.exitCode = 1;
            }
            return;
        }
        try {
            const r = installSessionStartHook(filePath);
            switch (r.status) {
                case 'installed':
                    printSuccess(`SessionStart context hook installed (${scope} scope)`);
                    printInfo('settings', r.filePath);
                    if (r.backupPath)
                        printInfo('backup', r.backupPath);
                    printInfo('next', 'open a new Claude Code session — Olam state will preload via additionalContext');
                    return;
                case 'already-present':
                    printInfo('sessionstart hook', `already installed at ${r.filePath}`);
                    return;
                case 'no-op':
                    printWarning(`no change made to ${r.filePath}`);
                    return;
            }
        }
        catch (err) {
            printError(`install failed: ${err instanceof Error ? err.message : String(err)}`);
            process.exitCode = 1;
        }
    });
}
//# sourceMappingURL=install-sessionstart-hook.js.map