/**
 * ExecResult factory functions for cleaner code.
 *
 * These helpers reduce verbosity and improve readability when
 * constructing ExecResult objects throughout the interpreter.
 */
import { ExecutionLimitError } from "../errors.js";
/**
 * A successful result with no output.
 * Use this for commands that succeed silently.
 */
export const OK = Object.freeze({
    stdout: "",
    stderr: "",
    exitCode: 0,
});
/**
 * Create a successful result with optional stdout.
 *
 * @param stdout - Output to include (default: "")
 * @param observations - Optional failure metadata
 * @returns ExecResult with exitCode 0
 */
export function success(stdout = "", observations) {
    return { stdout, stderr: "", exitCode: 0, observations };
}
/**
 * Create a failure result with stderr message.
 *
 * @param stderr - Error message to include
 * @param exitCode - Exit code (default: 1)
 * @param observations - Optional failure metadata
 * @returns ExecResult with the specified exitCode
 */
export function failure(stderr, exitCode = 1, observations) {
    return { stdout: "", stderr, exitCode, observations };
}
/**
 * Create a result with all fields specified.
 *
 * @param stdout - Standard output
 * @param stderr - Standard error
 * @param exitCode - Exit code
 * @param observations - Optional failure metadata
 * @returns ExecResult with all fields
 */
export function result(stdout, stderr, exitCode, observations) {
    return { stdout, stderr, exitCode, observations };
}
/**
 * Convert a boolean test result to an ExecResult.
 * Useful for test/conditional commands where true = exit 0, false = exit 1.
 *
 * @param passed - Boolean test result
 * @returns ExecResult with exitCode 0 if passed, 1 otherwise
 */
export function testResult(passed) {
    return { stdout: "", stderr: "", exitCode: passed ? 0 : 1 };
}
/**
 * Throw an ExecutionLimitError for execution limits (recursion, iterations, commands).
 *
 * @param message - Error message describing the limit exceeded
 * @param limitType - Type of limit exceeded
 * @param stdout - Accumulated stdout to include
 * @param stderr - Accumulated stderr to include
 * @throws ExecutionLimitError always
 */
export function throwExecutionLimit(message, limitType, stdout = "", stderr = "") {
    throw new ExecutionLimitError(message, limitType, stdout, stderr);
}
/**
 * Check file descriptor count against the limit before adding a new one.
 * Throws ExecutionLimitError if the limit would be exceeded.
 */
export function checkFdLimit(ctx) {
    const fds = ctx.state.fileDescriptors;
    if (fds && fds.size >= ctx.limits.maxFileDescriptors) {
        throw new ExecutionLimitError(`too many open file descriptors (max ${ctx.limits.maxFileDescriptors})`, "file_descriptors");
    }
}
export const OBSERVATION_CODES = Object.freeze({
    COMMAND_NOT_FOUND: "CMD_NOT_FOUND",
    FILE_NOT_FOUND: "ENOENT",
    DIRECTORY_NOT_FOUND: "ENOENT_DIR",
    PERMISSION_DENIED: "EACCES",
    IS_A_DIRECTORY: "EISDIR",
    NOT_A_DIRECTORY: "ENOTDIR",
    LIMIT_EXCEEDED: "ELIMIT",
    SECURITY_VIOLATION: "ESEC",
    SYNTAX_ERROR: "ESYNTAX",
});
/**
 * Observation factory helpers.
 *
 * These produce well-formed, high-confidence {@link Observation} objects at
 * the SOURCE of a typed failure — where the interpreter/command actually KNOWS
 * the cause (command resolution, fs ops, permission checks) rather than where
 * AgTrace regex-scrapes English stderr after the fact.
 *
 * Every source-emitted observation carries:
 *  - a stable machine `code` (see {@link OBSERVATION_CODES})
 *  - a `confidence` of 1.0 (the source is authoritative about its own failure)
 *
 * All factories return frozen, immutable objects (new objects, never mutated).
 */
export const obs = Object.freeze({
    /**
     * Command name could not be resolved on PATH (exit 127).
     * @param command - The unresolved command name.
     * @param suggestions - Optional "did you mean" candidates.
     */
    commandNotFound(command, suggestions) {
        return Object.freeze({
            type: "command_not_found",
            code: OBSERVATION_CODES.COMMAND_NOT_FOUND,
            confidence: 1,
            message: `Command '${command}' not found.`,
            command,
            ...(suggestions && suggestions.length > 0 ? { suggestions } : {}),
        });
    },
    /**
     * A file path did not exist when a command tried to read it.
     * @param path - The missing path (as the user referenced it).
     * @param command - The command that attempted the read.
     */
    fileNotFound(path, command) {
        return Object.freeze({
            type: "file_not_found",
            code: OBSERVATION_CODES.FILE_NOT_FOUND,
            confidence: 1,
            message: `File '${path}' not found.`,
            path,
            ...(command ? { command } : {}),
        });
    },
    /**
     * A directory path did not exist.
     * @param path - The missing directory path.
     * @param command - The command that attempted the operation.
     */
    directoryNotFound(path, command) {
        return Object.freeze({
            type: "directory_not_found",
            code: OBSERVATION_CODES.DIRECTORY_NOT_FOUND,
            confidence: 1,
            message: `Directory '${path}' not found.`,
            path,
            ...(command ? { command } : {}),
        });
    },
    /**
     * Expected a file but the path is a directory (EISDIR).
     * @param path - The path that is a directory.
     * @param command - The command that attempted the operation.
     */
    isADirectory(path, command) {
        return Object.freeze({
            type: "file_not_found",
            code: OBSERVATION_CODES.IS_A_DIRECTORY,
            confidence: 1,
            message: `'${path}' is a directory, not a file.`,
            path,
            ...(command ? { command } : {}),
        });
    },
    /**
     * A path component expected to be a directory is not (ENOTDIR).
     * @param path - The path that is not a directory.
     * @param command - The command that attempted the operation.
     */
    notADirectory(path, command) {
        return Object.freeze({
            type: "directory_not_found",
            code: OBSERVATION_CODES.NOT_A_DIRECTORY,
            confidence: 1,
            message: `'${path}' is not a directory.`,
            path,
            ...(command ? { command } : {}),
        });
    },
    /**
     * Access to a path was denied by the security policy or fs mode (exit 126).
     * @param path - The path that was denied.
     * @param command - The command that attempted the access.
     */
    permissionDenied(path, command) {
        return Object.freeze({
            type: "permission_denied",
            code: OBSERVATION_CODES.PERMISSION_DENIED,
            confidence: 1,
            message: `Permission denied for '${path}'.`,
            path,
            ...(command ? { command } : {}),
        });
    },
});
