/**
 * Unified Script Runtime
 *
 * Based on FCS runtime (class/test/assert/dict/lambda/interpolation/pipeline/ternary/index)
 * with Buddy Script additions:
 * - Async/await native support
 * - ForCStyle statement execution
 * - AwaitExpression handling
 * - Export statement handling
 * - Timeout enforcement per-statement
 * - ScriptResult with testResults tracking
 *
 * Extensions: .bs (primary), .fcs (backward-compatible alias)
 */
import { TokenType, DEFAULT_SCRIPT_CONFIG, } from './types.js';
import { createBuiltins } from './builtins.js';
import { createGrokBindings } from './codebuddy-bindings.js';
// Control flow signals
class ReturnSignal {
    value;
    constructor(value) {
        this.value = value;
    }
}
class BreakSignal {
}
class ContinueSignal {
}
export class FCSRuntime {
    config;
    globalContext;
    output = [];
    startTime = 0;
    testResults = [];
    constructor(config = {}) {
        this.config = { ...DEFAULT_SCRIPT_CONFIG, ...config };
        this.globalContext = {
            variables: new Map(),
            functions: new Map(),
        };
        this.initializeBuiltins();
    }
    initializeBuiltins() {
        const printFn = (msg) => {
            this.output.push(msg);
        };
        // Load unified builtins (merged FCS + BS)
        const builtins = createBuiltins(this.config, printFn);
        for (const [name, value] of Object.entries(builtins)) {
            this.globalContext.variables.set(name, value);
            if (typeof value === 'function') {
                this.globalContext.functions.set(name, value);
            }
        }
        // Load codebuddy bindings (grok.*, tool.*, context.*, agent.*, mcp.*, git.*, session.*)
        const grokBindings = createGrokBindings(this.config, printFn);
        for (const [name, value] of Object.entries(grokBindings)) {
            this.globalContext.variables.set(name, value);
        }
        // Inject user variables
        if (this.config.variables) {
            for (const [name, value] of Object.entries(this.config.variables)) {
                this.globalContext.variables.set(name, value);
            }
        }
    }
    async execute(program) {
        this.startTime = Date.now();
        this.output = [];
        this.testResults = [];
        let returnValue = null;
        let error;
        try {
            for (const statement of (program.statements || program.body || [])) {
                const result = await this.executeNode(statement, this.globalContext);
                if (result instanceof ReturnSignal) {
                    returnValue = result.value;
                    break;
                }
            }
        }
        catch (err) {
            if (err instanceof ReturnSignal) {
                returnValue = err.value;
            }
            else {
                error = err instanceof Error ? err.message : String(err);
            }
        }
        return {
            success: !error,
            output: this.output,
            error,
            returnValue,
            duration: Date.now() - this.startTime,
            testResults: this.testResults.length > 0 ? this.testResults : undefined,
        };
    }
    // Normalize old Buddy Script AST nodes to FCS format
    normalizeNode(node) {
        const n = node;
        // Map old BS type names to FCS type names
        const typeMap = {
            'VariableDeclaration': 'VarDeclaration',
            'BlockStatement': 'Block',
            'IfStatement': 'If',
            'WhileStatement': 'While',
            'ForInStatement': 'For',
            'ReturnStatement': 'Return',
            'BreakStatement': 'Break',
            'ContinueStatement': 'Continue',
            'TryStatement': 'Try',
            'ThrowStatement': 'Throw',
            'ExpressionStatement': 'ExpressionStmt',
            'BinaryExpression': 'Binary',
            'UnaryExpression': 'Unary',
            'LogicalExpression': 'Binary',
            'AssignmentExpression': 'Assignment',
            'CallExpression': 'Call',
            'MemberExpression': 'Member',
            'ArrayExpression': 'Array',
            'ObjectExpression': 'Dict',
            'ConditionalExpression': 'Ternary',
            'ArrowFunctionExpression': 'Lambda',
            'ArrowFunction': 'Lambda',
            'AwaitExpression': 'Await',
        };
        if (n.type === 'ForStatement') {
            // Detect C-style vs for-in
            n.type = (n.init !== undefined || n.test !== undefined || n.update !== undefined) ? 'ForCStyle' : 'For';
        }
        else if (typeMap[n.type]) {
            n.type = typeMap[n.type];
        }
        // Map old BS field names to FCS field names
        if (n.type === 'VarDeclaration' && n.init !== undefined && n.initializer === undefined) {
            n.initializer = n.init;
            if (n.kind)
                n.isConst = n.kind === 'const';
        }
        if (n.type === 'Block' && n.body && !n.statements) {
            n.statements = n.body;
        }
        if (n.type === 'If') {
            if (n.test && !n.condition)
                n.condition = n.test;
            if (n.consequent && !n.thenBranch)
                n.thenBranch = n.consequent;
            if (n.alternate !== undefined && n.elseBranch === undefined)
                n.elseBranch = n.alternate;
            if (n.body && !n.thenBranch)
                n.thenBranch = n.body;
        }
        if (n.type === 'While' && n.test && !n.condition) {
            n.condition = n.test;
        }
        // Map For (for-in): old BS uses left/right, FCS uses variable/iterable
        if (n.type === 'For') {
            if (n.left && !n.variable)
                n.variable = typeof n.left === 'object' ? n.left.name : n.left;
            if (n.right && !n.iterable)
                n.iterable = n.right;
        }
        if (n.type === 'Try') {
            if (n.block && !n.tryBlock)
                n.tryBlock = n.block;
            if (n.handler && !n.catchClauses) {
                n.catchClauses = [{
                        variable: n.handler.param || n.handler.variable,
                        body: n.handler.body || n.handler,
                    }];
            }
            if (n.finalizer && !n.finallyBlock)
                n.finallyBlock = n.finalizer;
        }
        if (n.type === 'Ternary') {
            if (n.test && !n.condition)
                n.condition = n.test;
        }
        if (n.type === 'Lambda' && n.params && !n.parameters) {
            n.parameters = n.params.map((p) => typeof p === 'string' ? p : p.name);
        }
        if (n.type === 'Return' && n.argument !== undefined && n.value === undefined) {
            n.value = n.argument;
        }
        if (n.type === 'Throw' && n.argument !== undefined && n.expression === undefined) {
            n.expression = n.argument;
        }
        // Map old-style operators ('+', '-', etc.) to TokenType names ('Plus', 'Minus', etc.)
        if ((n.type === 'Binary' || n.type === 'Unary' || n.type === 'Assignment') && n.operator) {
            const opMap = {
                '+': 'Plus', '-': 'Minus', '*': 'Multiply', '/': 'Divide', '%': 'Modulo', '**': 'Power',
                '==': 'Equal', '===': 'Equal', '!=': 'NotEqual', '!==': 'NotEqual', '<': 'Less', '>': 'Greater', '<=': 'LessEqual', '>=': 'GreaterEqual',
                '&&': 'And', '||': 'Or', '!': 'Not',
                '=': 'Assign', '+=': 'PlusAssign', '-=': 'MinusAssign', '*=': 'MultiplyAssign', '/=': 'DivideAssign',
            };
            if (opMap[n.operator])
                n.operator = opMap[n.operator];
        }
        // Map Unary: old BS uses 'argument', FCS uses 'operand'
        if (n.type === 'Unary' && n.argument && !n.operand) {
            n.operand = n.argument;
        }
        // Map FunctionDeclaration: old BS uses 'params', FCS uses 'parameters'
        if (n.type === 'FunctionDeclaration' && n.params && !n.parameters) {
            n.parameters = n.params.map((p) => {
                if (typeof p === 'string')
                    return { name: p };
                return { name: p.name, defaultValue: p.default || p.defaultValue };
            });
        }
        // Map Assignment fields: old BS uses left/right, FCS uses target/value
        if (n.type === 'Assignment') {
            if (n.left && !n.target)
                n.target = n.left;
            if (n.right !== undefined && n.value === undefined)
                n.value = n.right;
        }
        // Call: callee and arguments are the same in both formats
        // Map old 'object'/'property' to FCS 'object'/'member' for MemberExpression
        if (n.type === 'Member' && n.property !== undefined && n.member === undefined) {
            // Old BS property can be an AstNode (e.g., {type: 'Identifier', name: 'foo'}) or a string
            n.member = typeof n.property === 'object' ? (n.property.name || n.property.value) : n.property;
        }
        // Map 'elements' to 'elements' (same for arrays)
        // Map 'properties' to 'elements' Map for objects/dicts
        if (n.type === 'Dict' && n.properties && !(n.elements instanceof Map)) {
            const map = new Map();
            for (const p of n.properties) {
                const key = typeof p.key === 'object' ? (p.key.name || p.key.value) : p.key;
                map.set(String(key), p.value);
            }
            n.elements = map;
        }
        return n;
    }
    async executeNode(node, ctx) {
        // Check timeout
        if (Date.now() - this.startTime > this.config.timeout) {
            throw new Error(`Script timeout after ${this.config.timeout}ms`);
        }
        // Normalize old BS AST format
        node = this.normalizeNode(node);
        switch (node.type) {
            // Declarations
            case 'VarDeclaration':
                return this.executeVarDeclaration(node, ctx);
            case 'FunctionDeclaration':
                return this.executeFunctionDeclaration(node, ctx);
            case 'ClassDeclaration':
                return this.executeClassDeclaration(node, ctx);
            case 'TestDeclaration':
                return this.executeTestDeclaration(node, ctx);
            // Statements
            case 'Block':
                return this.executeBlock(node, ctx);
            case 'ExpressionStmt':
                return this.evaluate(node.expression, ctx);
            case 'If':
                return this.executeIfStatement(node, ctx);
            case 'While':
                return this.executeWhileStatement(node, ctx);
            case 'For':
                return this.executeForStatement(node, ctx);
            case 'ForCStyle':
                return this.executeForCStyleStatement(node, ctx);
            case 'Return':
                return this.executeReturnStatement(node, ctx);
            case 'Break':
                return new BreakSignal();
            case 'Continue':
                return new ContinueSignal();
            case 'Try':
                return this.executeTryStatement(node, ctx);
            case 'Throw':
                return this.executeThrowStatement(node, ctx);
            case 'Import':
                return this.executeImportStatement(node, ctx);
            case 'Export':
                return this.executeExportStatement(node, ctx);
            case 'Assert':
                return this.executeAssertStatement(node, ctx);
            // Expressions (delegated to evaluate)
            default:
                return this.evaluate(node, ctx);
        }
    }
    // ============================================
    // Statement Execution
    // ============================================
    async executeVarDeclaration(node, ctx) {
        const value = node.initializer
            ? await this.evaluate(node.initializer, ctx)
            : null;
        ctx.variables.set(node.name, value);
        return null;
    }
    async executeFunctionDeclaration(node, ctx) {
        const fn = async (...args) => {
            const localCtx = {
                variables: new Map(),
                functions: new Map(ctx.functions),
                parent: ctx,
            };
            // Bind parameters (with named args support)
            const lastArg = args.length > 0 ? args[args.length - 1] : null;
            const hasNamedArgs = lastArg !== null && typeof lastArg === 'object' && lastArg !== null && '__namedArgs' in lastArg;
            const namedArgs = hasNamedArgs
                ? args.pop().__namedArgs
                : {};
            for (let i = 0; i < node.parameters.length; i++) {
                const param = node.parameters[i];
                let value;
                if (namedArgs[param.name] !== undefined) {
                    value = namedArgs[param.name];
                }
                else if (args[i] !== undefined) {
                    value = args[i];
                }
                else if (param.defaultValue) {
                    value = await this.evaluate(param.defaultValue, ctx);
                }
                else {
                    value = null;
                }
                localCtx.variables.set(param.name, value);
            }
            try {
                const result = await this.executeBlock(node.body, localCtx);
                if (result instanceof ReturnSignal) {
                    return result.value;
                }
                return null;
            }
            catch (err) {
                if (err instanceof ReturnSignal) {
                    return err.value;
                }
                throw err;
            }
        };
        ctx.functions.set(node.name, fn);
        ctx.variables.set(node.name, fn);
        return null;
    }
    async executeClassDeclaration(node, ctx) {
        const classConstructor = async (...args) => {
            const instance = {};
            for (const member of node.members) {
                if (member.type === 'VarDeclaration') {
                    const varDecl = member;
                    instance[varDecl.name] = varDecl.initializer
                        ? await this.evaluate(varDecl.initializer, ctx)
                        : null;
                }
                else if (member.type === 'FunctionDeclaration') {
                    const fnDecl = member;
                    instance[fnDecl.name] = async (...methodArgs) => {
                        const methodCtx = {
                            variables: new Map([['this', instance]]),
                            functions: new Map(ctx.functions),
                            parent: ctx,
                        };
                        for (let i = 0; i < fnDecl.parameters.length; i++) {
                            methodCtx.variables.set(fnDecl.parameters[i].name, methodArgs[i]);
                        }
                        const result = await this.executeBlock(fnDecl.body, methodCtx);
                        if (result instanceof ReturnSignal) {
                            return result.value;
                        }
                        return null;
                    };
                }
            }
            if (instance.constructor && typeof instance.constructor === 'function') {
                await instance.constructor(...args);
            }
            return instance;
        };
        ctx.variables.set(node.name, classConstructor);
        return null;
    }
    async executeTestDeclaration(node, ctx) {
        if (!this.config.verbose) {
            return null;
        }
        try {
            await this.executeBlock(node.body, ctx);
            this.testResults.push({ name: node.name, passed: true });
            this.output.push(`✓ ${node.name}`);
        }
        catch (err) {
            const errorMsg = err instanceof Error ? err.message : String(err);
            this.testResults.push({ name: node.name, passed: false, error: errorMsg });
            this.output.push(`✗ ${node.name}: ${errorMsg}`);
        }
        return null;
    }
    async executeBlock(node, ctx) {
        for (const stmt of (node.statements || node.body || [])) {
            const result = await this.executeNode(stmt, ctx);
            if (result instanceof ReturnSignal ||
                result instanceof BreakSignal ||
                result instanceof ContinueSignal) {
                return result;
            }
        }
        return null;
    }
    async executeIfStatement(node, ctx) {
        const condition = await this.evaluate(node.condition, ctx);
        if (this.isTruthy(condition)) {
            return this.executeNode(node.thenBranch, ctx);
        }
        else if (node.elseBranch) {
            return this.executeNode(node.elseBranch, ctx);
        }
        return null;
    }
    async executeWhileStatement(node, ctx) {
        while (this.isTruthy(await this.evaluate(node.condition, ctx))) {
            const result = await this.executeNode(node.body, ctx);
            if (result instanceof ReturnSignal)
                return result;
            if (result instanceof BreakSignal)
                break;
            if (result instanceof ContinueSignal)
                continue;
        }
        return null;
    }
    async executeForStatement(node, ctx) {
        const iterable = await this.evaluate(node.iterable, ctx);
        if (!Array.isArray(iterable) && typeof iterable !== 'object') {
            throw new Error('for-in requires an iterable (array or object)');
        }
        const items = Array.isArray(iterable)
            ? iterable
            : Object.entries(iterable);
        const localCtx = {
            variables: new Map(),
            functions: ctx.functions,
            parent: ctx,
        };
        for (const item of items) {
            localCtx.variables.set(node.variable, item);
            const result = await this.executeNode(node.body, localCtx);
            if (result instanceof ReturnSignal)
                return result;
            if (result instanceof BreakSignal)
                break;
            if (result instanceof ContinueSignal)
                continue;
        }
        return null;
    }
    async executeForCStyleStatement(node, ctx) {
        const localCtx = {
            variables: new Map(),
            functions: ctx.functions,
            parent: ctx,
        };
        // Execute init (normalize in case of old BS format)
        if (node.init) {
            const initNode = this.normalizeNode(node.init);
            if (initNode.type === 'VarDeclaration') {
                await this.executeVarDeclaration(initNode, localCtx);
            }
            else {
                await this.evaluate(initNode, localCtx);
            }
        }
        // Loop while test is true
        while (node.test === null || this.isTruthy(await this.evaluate(node.test, localCtx))) {
            const result = await this.executeNode(node.body, localCtx);
            if (result instanceof ReturnSignal)
                return result;
            if (result instanceof BreakSignal)
                break;
            if (result instanceof ContinueSignal) {
                if (node.update) {
                    await this.evaluate(node.update, localCtx);
                }
                continue;
            }
            if (node.update) {
                await this.evaluate(node.update, localCtx);
            }
        }
        return null;
    }
    async executeReturnStatement(node, ctx) {
        const value = node.value ? await this.evaluate(node.value, ctx) : null;
        return new ReturnSignal(value);
    }
    async executeTryStatement(node, ctx) {
        try {
            return await this.executeBlock(node.tryBlock, ctx);
        }
        catch (err) {
            for (const clause of node.catchClauses) {
                const localCtx = {
                    variables: new Map(ctx.variables),
                    functions: ctx.functions,
                    parent: ctx,
                };
                if (clause.variable) {
                    const errorValue = err instanceof Error
                        ? { message: err.message, stack: err.stack }
                        : err;
                    localCtx.variables.set(clause.variable, errorValue);
                }
                return this.executeBlock(clause.body, localCtx);
            }
            throw err;
        }
        finally {
            if (node.finallyBlock) {
                await this.executeBlock(node.finallyBlock, ctx);
            }
        }
    }
    async executeThrowStatement(node, ctx) {
        const value = await this.evaluate(node.expression, ctx);
        if (typeof value === 'string') {
            throw new Error(value);
        }
        throw value;
    }
    async executeImportStatement(_node, _ctx) {
        // Imports are handled at initialization or by module system
        return null;
    }
    async executeExportStatement(node, ctx) {
        // Execute the declaration normally - exports don't change runtime behavior
        return this.executeNode(node.declaration, ctx);
    }
    async executeAssertStatement(node, ctx) {
        const condition = await this.evaluate(node.condition, ctx);
        if (!this.isTruthy(condition)) {
            const message = node.message || 'Assertion failed';
            throw new Error(message);
        }
        return null;
    }
    // ============================================
    // Expression Evaluation
    // ============================================
    async evaluate(node, ctx) {
        node = this.normalizeNode(node);
        switch (node.type) {
            case 'Literal':
                return node.value;
            case 'Identifier':
                return this.lookupVariable(node.name, ctx);
            case 'Binary':
                return this.evaluateBinary(node, ctx);
            case 'Unary':
                return this.evaluateUnary(node, ctx);
            case 'Assignment':
                return this.evaluateAssignment(node, ctx);
            case 'Call':
                return this.evaluateCall(node, ctx);
            case 'Member':
                return this.evaluateMember(node, ctx);
            case 'Index':
                return this.evaluateIndex(node, ctx);
            case 'Array':
                return this.evaluateArray(node, ctx);
            case 'Dict':
                return this.evaluateDict(node, ctx);
            case 'Lambda':
                return this.evaluateLambda(node, ctx);
            case 'Interpolation':
                return this.evaluateInterpolation(node, ctx);
            case 'Ternary':
                return this.evaluateTernary(node, ctx);
            case 'Await':
                // Await just evaluates the argument (our runtime is already async)
                return this.evaluate(node.argument, ctx);
            default:
                throw new Error(`Unknown expression type: ${node.type}`);
        }
    }
    lookupVariable(name, ctx) {
        // Walk up scope chain checking variables first
        let current = ctx;
        while (current) {
            if (current.variables.has(name)) {
                return current.variables.get(name);
            }
            current = current.parent;
        }
        // Then check functions (builtins)
        if (ctx.functions.has(name)) {
            return ctx.functions.get(name);
        }
        throw new Error(`Undefined variable: ${name}`);
    }
    setVariable(name, value, ctx) {
        let current = ctx;
        while (current) {
            if (current.variables.has(name)) {
                current.variables.set(name, value);
                return;
            }
            current = current.parent;
        }
        ctx.variables.set(name, value);
    }
    async evaluateBinary(node, ctx) {
        const left = await this.evaluate(node.left, ctx);
        const right = await this.evaluate(node.right, ctx);
        switch (node.operator) {
            case TokenType.Plus:
                if (typeof left === 'string' || typeof right === 'string') {
                    return String(left) + String(right);
                }
                return left + right;
            case TokenType.Minus:
                return left - right;
            case TokenType.Multiply:
                if (typeof left === 'string' && typeof right === 'number') {
                    return left.repeat(right);
                }
                return left * right;
            case TokenType.Divide:
                return left / right;
            case TokenType.Modulo:
                return left % right;
            case TokenType.Power:
                return Math.pow(left, right);
            case TokenType.Equal:
                return left === right;
            case TokenType.NotEqual:
                return left !== right;
            case TokenType.Less:
                return left < right;
            case TokenType.LessEqual:
                return left <= right;
            case TokenType.Greater:
                return left > right;
            case TokenType.GreaterEqual:
                return left >= right;
            case TokenType.And:
                return this.isTruthy(left) && this.isTruthy(right);
            case TokenType.Or:
                return this.isTruthy(left) || this.isTruthy(right);
            default:
                throw new Error(`Unknown binary operator: ${node.operator}`);
        }
    }
    async evaluateUnary(node, ctx) {
        const operand = await this.evaluate(node.operand, ctx);
        switch (node.operator) {
            case TokenType.Minus:
                return -operand;
            case TokenType.Not:
                return !this.isTruthy(operand);
            default:
                throw new Error(`Unknown unary operator: ${node.operator}`);
        }
    }
    async evaluateAssignment(node, ctx) {
        let value = await this.evaluate(node.value, ctx);
        if (node.operator !== TokenType.Assign) {
            const current = await this.evaluate(node.target, ctx);
            switch (node.operator) {
                case TokenType.PlusAssign:
                    if (typeof current === 'string' || typeof value === 'string') {
                        value = String(current) + String(value);
                    }
                    else {
                        value = current + value;
                    }
                    break;
                case TokenType.MinusAssign:
                    value = current - value;
                    break;
                case TokenType.MultiplyAssign:
                    value = current * value;
                    break;
                case TokenType.DivideAssign:
                    value = current / value;
                    break;
            }
        }
        if (node.target.type === 'Identifier') {
            this.setVariable(node.target.name, value, ctx);
        }
        else if (node.target.type === 'Member') {
            const member = node.target;
            const object = await this.evaluate(member.object, ctx);
            object[member.member] = value;
        }
        else if (node.target.type === 'Index') {
            const index = node.target;
            const object = await this.evaluate(index.object, ctx);
            const key = await this.evaluate(index.index, ctx);
            object[key] = value;
        }
        return value;
    }
    async evaluateCall(node, ctx) {
        const callee = await this.evaluate(node.callee, ctx);
        if (typeof callee !== 'function') {
            throw new Error(`${JSON.stringify(callee)} is not a function`);
        }
        const args = [];
        for (const arg of node.arguments) {
            args.push(await this.evaluate(arg, ctx));
        }
        // Handle named arguments
        if (node.namedArgs && Object.keys(node.namedArgs).length > 0) {
            const namedValues = {};
            for (const [name, expr] of Object.entries(node.namedArgs)) {
                namedValues[name] = await this.evaluate(expr, ctx);
            }
            args.push({ __namedArgs: namedValues });
        }
        const result = callee(...args);
        return result instanceof Promise ? await result : result;
    }
    async evaluateMember(node, ctx) {
        const object = await this.evaluate(node.object, ctx);
        if (object === null || object === undefined) {
            throw new Error('Cannot read property of null or undefined');
        }
        const obj = object;
        const value = obj[node.member];
        if (typeof value === 'function') {
            return value.bind(obj);
        }
        return value;
    }
    async evaluateIndex(node, ctx) {
        const object = await this.evaluate(node.object, ctx);
        const index = await this.evaluate(node.index, ctx);
        if (object === null || object === undefined) {
            throw new Error('Cannot index null or undefined');
        }
        return object[index];
    }
    async evaluateArray(node, ctx) {
        const elements = [];
        for (const element of node.elements) {
            elements.push(await this.evaluate(element, ctx));
        }
        return elements;
    }
    async evaluateDict(node, ctx) {
        const dict = {};
        for (const [key, valueNode] of node.elements) {
            dict[key] = await this.evaluate(valueNode, ctx);
        }
        return dict;
    }
    evaluateLambda(node, ctx) {
        return async (...args) => {
            const localCtx = {
                variables: new Map(),
                functions: ctx.functions,
                parent: ctx,
            };
            for (let i = 0; i < node.parameters.length; i++) {
                localCtx.variables.set(node.parameters[i], args[i]);
            }
            if (node.body.type === 'Block') {
                const result = await this.executeBlock(node.body, localCtx);
                if (result instanceof ReturnSignal) {
                    return result.value;
                }
                return null;
            }
            return this.evaluate(node.body, localCtx);
        };
    }
    async evaluateInterpolation(node, ctx) {
        let result = '';
        for (const part of node.parts) {
            const value = await this.evaluate(part, ctx);
            result += String(value);
        }
        return result;
    }
    async evaluateTernary(node, ctx) {
        const condition = await this.evaluate(node.condition, ctx);
        if (this.isTruthy(condition)) {
            return this.evaluate(node.consequent, ctx);
        }
        return this.evaluate(node.alternate, ctx);
    }
    // ============================================
    // Helpers
    // ============================================
    isTruthy(value) {
        if (value === null || value === undefined || value === false || value === 0 || value === '') {
            return false;
        }
        return true;
    }
    getTestResults() {
        return this.testResults;
    }
    getOutput() {
        return this.output;
    }
}
/** @deprecated Use FCSRuntime instead */
export const Runtime = FCSRuntime;
export function createRuntime(config = {}) {
    return new FCSRuntime(config);
}
//# sourceMappingURL=runtime.js.map