import { assertDefenseContext, SecurityViolationError, } from "../security/defense-context.js";
function isPromiseLike(value) {
    return (value !== null &&
        typeof value === "object" &&
        "then" in value &&
        typeof value.then === "function");
}
function wrapFunction(fn, requireDefenseContext, component, phase, ctx) {
    return ((...args) => {
        // If this is a write-related operation and we are in plan mode, block it.
        // This provides defense-in-depth for all shell commands (touch, rm, mkdir, etc.)
        const isWriteOp = /^(writeFile|appendFile|mkdir|rm|cp|mv|chmod|symlink|link|utimes|restore)$/.test(phase.replace(/^fs\./, ""));
        if (isWriteOp && ctx?.bash?.getMode?.() === "plan") {
            const message = `security violation: Destructive operation '${phase}' blocked in 'plan' mode.`;
            const error = new SecurityViolationError(message, {
                timestamp: Date.now(),
                type: "plan_mode_violation",
                message,
                path: phase,
            });
            error.name = "SecurityViolationError";
            throw error;
        }
        assertDefenseContext(requireDefenseContext, component, `${phase} call`);
        const result = fn(...args);
        if (isPromiseLike(result)) {
            return result.then((value) => {
                assertDefenseContext(requireDefenseContext, component, `${phase} post-await`);
                return value;
            }, (error) => {
                assertDefenseContext(requireDefenseContext, component, `${phase} post-await`);
                throw error;
            });
        }
        assertDefenseContext(requireDefenseContext, component, `${phase} return`);
        return result;
    });
}
function wrapFileSystem(fs, requireDefenseContext, component, ctx) {
    const wrappedFs = {
        readFile: wrapFunction(fs.readFile.bind(fs), requireDefenseContext, component, "fs.readFile", ctx),
        readFileBuffer: wrapFunction(fs.readFileBuffer.bind(fs), requireDefenseContext, component, "fs.readFileBuffer", ctx),
        writeFile: wrapFunction(fs.writeFile.bind(fs), requireDefenseContext, component, "fs.writeFile", ctx),
        appendFile: wrapFunction(fs.appendFile.bind(fs), requireDefenseContext, component, "fs.appendFile", ctx),
        exists: wrapFunction(fs.exists.bind(fs), requireDefenseContext, component, "fs.exists", ctx),
        stat: wrapFunction(fs.stat.bind(fs), requireDefenseContext, component, "fs.stat", ctx),
        mkdir: wrapFunction(fs.mkdir.bind(fs), requireDefenseContext, component, "fs.mkdir", ctx),
        readdir: wrapFunction(fs.readdir.bind(fs), requireDefenseContext, component, "fs.readdir", ctx),
        rm: wrapFunction(fs.rm.bind(fs), requireDefenseContext, component, "fs.rm", ctx),
        cp: wrapFunction(fs.cp.bind(fs), requireDefenseContext, component, "fs.cp", ctx),
        mv: wrapFunction(fs.mv.bind(fs), requireDefenseContext, component, "fs.mv", ctx),
        resolvePath: wrapFunction(fs.resolvePath.bind(fs), requireDefenseContext, component, "fs.resolvePath", ctx),
        getAllPaths: wrapFunction(fs.getAllPaths.bind(fs), requireDefenseContext, component, "fs.getAllPaths", ctx),
        chmod: wrapFunction(fs.chmod.bind(fs), requireDefenseContext, component, "fs.chmod", ctx),
        symlink: wrapFunction(fs.symlink.bind(fs), requireDefenseContext, component, "fs.symlink", ctx),
        link: wrapFunction(fs.link.bind(fs), requireDefenseContext, component, "fs.link", ctx),
        readlink: wrapFunction(fs.readlink.bind(fs), requireDefenseContext, component, "fs.readlink", ctx),
        lstat: wrapFunction(fs.lstat.bind(fs), requireDefenseContext, component, "fs.lstat", ctx),
        realpath: wrapFunction(fs.realpath.bind(fs), requireDefenseContext, component, "fs.realpath", ctx),
        utimes: wrapFunction(fs.utimes.bind(fs), requireDefenseContext, component, "fs.utimes", ctx),
        snapshot: wrapFunction(fs.snapshot.bind(fs), requireDefenseContext, component, "fs.snapshot", ctx),
        restore: wrapFunction(fs.restore.bind(fs), requireDefenseContext, component, "fs.restore", ctx),
    };
    if (fs.readdirWithFileTypes) {
        wrappedFs.readdirWithFileTypes = wrapFunction(fs.readdirWithFileTypes.bind(fs), requireDefenseContext, component, "fs.readdirWithFileTypes", ctx);
    }
    if (fs.toRealPath) {
        wrappedFs.toRealPath = wrapFunction(fs.toRealPath.bind(fs), requireDefenseContext, component, "fs.toRealPath", ctx);
    }
    return wrappedFs;
}
/**
 * Wrap command context APIs so async boundaries are fail-closed if defense
 * context is expected but missing.
 */
export function createDefenseAwareCommandContext(ctx, commandName) {
    if (!ctx.requireDefenseContext && !ctx.bash?.agentic) {
        return ctx;
    }
    const component = `command:${commandName}`;
    const wrappedCtx = {
        ...ctx,
        fs: wrapFileSystem(ctx.fs, ctx.requireDefenseContext, component, ctx),
    };
    if (ctx.exec) {
        wrappedCtx.exec = wrapFunction(ctx.exec, ctx.requireDefenseContext, component, "exec", ctx);
    }
    if (ctx.fetch) {
        wrappedCtx.fetch = wrapFunction(ctx.fetch, ctx.requireDefenseContext, component, "fetch", ctx);
    }
    if (ctx.sleep) {
        wrappedCtx.sleep = wrapFunction(ctx.sleep, ctx.requireDefenseContext, component, "sleep", ctx);
    }
    if (ctx.getRegisteredCommands) {
        wrappedCtx.getRegisteredCommands = wrapFunction(ctx.getRegisteredCommands, ctx.requireDefenseContext, component, "getRegisteredCommands", ctx);
    }
    return wrappedCtx;
}
