/**
 * shell-rc.ts — Idempotent shell-rc file append + timestamped backup.
 *
 * Phase C1 of olam-operator-onboarding-parity (plan
 * ~/.claude/plans/olam-operator-onboarding-parity.md).
 *
 * Phase C's `olam setup` wizard offers to add
 *   eval "$(olam completion zsh)"
 * to the operator's `~/.zshrc` / `~/.bashrc`. This module is the safety
 * layer that:
 *
 *   1. Grep-checks a marker substring before writing — re-running the
 *      wizard is a no-op (no duplicate eval lines, no n-times appended).
 *   2. Backs the file up to `<rcPath>.olam-bak.<ISO-timestamp>` before
 *      ANY mutation — recoverable if the operator decides they didn't
 *      want the line.
 *   3. Writes atomically via tmp-file + rename — `kill -9` mid-write
 *      can't leave a half-written rc file.
 *
 * Decision 6 (plan): mirrors adb's `phase_mise_activation` pattern; the
 * idempotency + backup pair is the operator-UX floor.
 *
 * Risk T4 mitigation (per plan + design doc): the grep-for-marker check
 * uses `String.includes` not a per-line scan — a marker can match across
 * whitespace if needed. The backup runs unconditionally before the
 * append; corrupted rc states are recoverable via `mv <backup> <rcPath>`.
 */
import { copyFileSync, existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
import path from 'node:path';
/**
 * Append `contentLine` to `rcPath` exactly once.
 *
 *   - rcPath doesn't exist           → returns `{status: 'no-rc-file', backupPath: null}`
 *   - rcPath content already includes the marker substring
 *                                    → returns `{status: 'already-present', backupPath: null}`
 *   - otherwise: backup + append
 *                                    → returns `{status: 'appended', backupPath: <ts-path>}`
 *
 * The write is atomic: content is staged to `<rcPath>.olam-tmp.<pid>`
 * and then `renameSync`'d to `rcPath`. POSIX rename is atomic within a
 * filesystem; a `kill -9` mid-call leaves either the backup-only state
 * (no rc change) or the renamed-final state (atomic apply), never a
 * half-written rc.
 *
 * The backup path uses a filesystem-safe ISO timestamp with `:` + `.`
 * replaced by `-` (avoids colons in filenames on Windows for cross-
 * platform consumers; not strictly required on macOS / Linux but
 * cosmetically nicer).
 */
export function appendIdempotent(opts) {
    const { rcPath, marker, contentLine, clock = () => new Date() } = opts;
    if (!existsSync(rcPath)) {
        return { status: 'no-rc-file', backupPath: null };
    }
    const content = readFileSync(rcPath, 'utf-8');
    if (content.includes(marker)) {
        return { status: 'already-present', backupPath: null };
    }
    const timestamp = clock().toISOString().replace(/[:.]/g, '-');
    const backupPath = `${rcPath}.olam-bak.${timestamp}`;
    copyFileSync(rcPath, backupPath);
    const separator = content.length === 0 || content.endsWith('\n') ? '' : '\n';
    const trailing = contentLine.endsWith('\n') ? '' : '\n';
    const nextContent = `${content}${separator}${contentLine}${trailing}`;
    // Atomic write: tmp + rename. POSIX rename is atomic within filesystem.
    const tmpPath = `${rcPath}.olam-tmp.${process.pid}`;
    writeFileSync(tmpPath, nextContent, { encoding: 'utf-8' });
    renameSync(tmpPath, rcPath);
    return { status: 'appended', backupPath };
}
/**
 * Resolve the operator's shell rc path based on `$SHELL`.
 *
 *   /bin/zsh, /usr/bin/zsh, /opt/homebrew/bin/zsh → ~/.zshrc
 *   /bin/bash, /usr/bin/bash                     → ~/.bashrc
 *   anything else                                → null (caller must surface)
 *
 * The caller decides what to do on null — typically Phase C surfaces a
 * remedy message naming the supported shells.
 */
export function resolveShellRc(home, shellEnv) {
    if (!shellEnv)
        return null;
    const basename = path.basename(shellEnv);
    if (basename === 'zsh')
        return path.join(home, '.zshrc');
    if (basename === 'bash')
        return path.join(home, '.bashrc');
    return null;
}
//# sourceMappingURL=shell-rc.js.map