/**
 * Programmatic fix handler registry.
 * Provides matchHandler(), resolveHandlerChain(), executeHandlerChain() for
 * replacing shell-metachar-blocked fixCommands with safe TypeScript handlers.
 *
 * Handler registration order: sysctl, fileAppend, fileWrite, packageInstall, chmodChown, sedReplace, aptUpgrade, systemctl
 */
import { sysctlHandler } from "./sysctl.js";
import { fileAppendHandler } from "./fileAppend.js";
import { fileWriteHandler } from "./fileWrite.js";
import { packageInstallHandler } from "./packageInstall.js";
import { chmodChownHandler } from "./chmodChown.js";
import { sedReplaceHandler } from "./sedReplace.js";
import { aptUpgradeHandler } from "./aptUpgrade.js";
import { systemctlHandler } from "./systemctl.js";
// ─── Handler registry (order matters — first match wins) ──────────────────────
const HANDLERS = [
    sysctlHandler,
    fileAppendHandler,
    fileWriteHandler,
    packageInstallHandler,
    chmodChownHandler,
    sedReplaceHandler,
    aptUpgradeHandler,
    systemctlHandler,
];
// ─── matchHandler ──────────────────────────────────────────────────────────────
/**
 * Returns the first matching handler+params pair, or null if no handler matches.
 */
export function matchHandler(fixCommand) {
    for (const handler of HANDLERS) {
        const params = handler.match(fixCommand);
        if (params !== null) {
            return { handler, params };
        }
    }
    return null;
}
// ─── resolveHandlerChain ───────────────────────────────────────────────────────
/**
 * Splits a compound fixCommand on ' && ', resolves each part to a handler.
 * Returns null if ANY part is unmatched (D-15 — no partial dispatch).
 */
export function resolveHandlerChain(fixCommand) {
    const parts = fixCommand
        .split(" && ")
        .map((p) => p.trim())
        .filter((p) => p.length > 0);
    const chain = [];
    for (const part of parts) {
        const match = matchHandler(part);
        if (match === null)
            return null;
        chain.push(match);
    }
    return chain.length > 0 ? chain : null;
}
// ─── executeHandlerChain ───────────────────────────────────────────────────────
/**
 * Executes each step in the chain sequentially.
 * If any step fails, rolls back all previously collected rollback steps in reverse order.
 * Implements atomic rollback (D-16).
 */
export async function executeHandlerChain(ip, chain) {
    const rollbackSteps = [];
    let lastDiff;
    for (const { handler, params } of chain) {
        const result = await handler.execute(ip, params);
        if (!result.success) {
            // Rollback in reverse order (D-16)
            for (const step of [...rollbackSteps].reverse()) {
                try {
                    await step.rollback(ip);
                }
                catch {
                    // Best-effort rollback — swallow errors to not mask the original failure
                }
            }
            return { success: false, error: result.error };
        }
        if (result.rollbackStep) {
            rollbackSteps.push(result.rollbackStep);
        }
        if (result.diff !== undefined) {
            lastDiff = result.diff;
        }
    }
    return { success: true, diff: lastDiff };
}
// ─── tryHandlerDispatch ─────────────────────────────────────���─────────────────
/**
 * Attempts handler dispatch for a fix command.
 * Returns { handled: true, diff? } if a handler matched (success or failure),
 * { handled: false } if no handler matched (caller should fall through to shell path).
 * Shared by runFix(), fixSafeCommand(), and handleServerFix() to avoid copy-paste.
 */
export async function tryHandlerDispatch(ip, check, applied, errors) {
    const chain = resolveHandlerChain(check.fixCommand);
    if (chain === null)
        return { handled: false };
    const result = await executeHandlerChain(ip, chain);
    if (result.success) {
        applied.push(check.id);
    }
    else {
        errors.push(`${check.id}: handler failed — ${result.error ?? "unknown"}`);
    }
    return { handled: true, diff: result.diff };
}
//# sourceMappingURL=index.js.map