/**
 * Skill Executor
 *
 * Executes skills by interpreting their content and invoking tools.
 */
import { EventEmitter } from 'events';
export const DEFAULT_EXECUTOR_CONFIG = {
    timeout: 60000,
    stepMode: false,
};
// ============================================================================
// Skill Executor Class
// ============================================================================
export class SkillExecutor extends EventEmitter {
    config;
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_EXECUTOR_CONFIG, ...config };
    }
    // ==========================================================================
    // Execution
    // ==========================================================================
    /**
     * Execute a skill
     */
    async execute(skill, context) {
        const startTime = Date.now();
        const toolCalls = [];
        try {
            // Check requirements
            const reqCheck = this.checkRequirements(skill, context);
            if (!reqCheck.met) {
                return {
                    success: false,
                    error: `Requirements not met: ${reqCheck.missing.join(', ')}`,
                    duration: Date.now() - startTime,
                };
            }
            // Execute based on skill content
            let output;
            // If skill has explicit steps, execute them
            if (skill.content.steps && skill.content.steps.length > 0) {
                output = await this.executeSteps(skill.content.steps, context, toolCalls);
            }
            // If skill has tool invocations, execute them
            else if (skill.content.tools && skill.content.tools.length > 0) {
                output = await this.executeTools(skill.content.tools, context, toolCalls);
            }
            // If skill has code blocks, execute them
            else if (skill.content.codeBlocks && skill.content.codeBlocks.length > 0) {
                output = await this.executeCodeBlocks(skill.content.codeBlocks, context);
            }
            // Otherwise, return the skill description as guidance
            else {
                output = this.formatSkillGuidance(skill, context);
            }
            return {
                success: true,
                output,
                toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
                duration: Date.now() - startTime,
            };
        }
        catch (error) {
            return {
                success: false,
                error: error instanceof Error ? error.message : String(error),
                toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
                duration: Date.now() - startTime,
            };
        }
    }
    /**
     * Check if skill requirements are met
     */
    checkRequirements(skill, context) {
        const missing = [];
        if (!skill.metadata.requires) {
            return { met: true, missing: [] };
        }
        // Check required tools
        if (skill.metadata.requires.tools) {
            for (const tool of skill.metadata.requires.tools) {
                if (!context.tools?.includes(tool)) {
                    missing.push(`tool:${tool}`);
                }
            }
        }
        // Check required environment variables
        if (skill.metadata.requires.env) {
            for (const envVar of skill.metadata.requires.env) {
                if (!context.env?.[envVar] && !process.env[envVar]) {
                    missing.push(`env:${envVar}`);
                }
            }
        }
        return { met: missing.length === 0, missing };
    }
    // ==========================================================================
    // Step Execution
    // ==========================================================================
    /**
     * Execute skill steps
     */
    async executeSteps(steps, context, toolCalls) {
        const outputs = [];
        const stepContext = {
            request: context.request,
            ...context.config,
        };
        for (const step of steps) {
            // Check condition
            if (step.condition && !this.evaluateCondition(step.condition, stepContext)) {
                continue;
            }
            this.emit('step:start', step.index, step.description);
            // Execute tool if specified
            if (step.tool && this.config.toolExecutor) {
                const args = this.interpolateArgs({}, stepContext);
                const result = await this.config.toolExecutor(step.tool, args);
                toolCalls.push({ tool: step.tool, args, result });
                stepContext[`step${step.index}`] = result;
                outputs.push(`Step ${step.index + 1}: ${step.description}\nResult: ${JSON.stringify(result)}`);
            }
            // Execute code if specified
            else if (step.code && this.config.codeExecutor) {
                const result = await this.config.codeExecutor(step.code, 'javascript', stepContext);
                stepContext[`step${step.index}`] = result;
                outputs.push(`Step ${step.index + 1}: ${step.description}\nResult: ${JSON.stringify(result)}`);
            }
            // Otherwise just note the step
            else {
                outputs.push(`Step ${step.index + 1}: ${step.description}`);
            }
            this.emit('step:complete', step.index, stepContext[`step${step.index}`]);
        }
        return outputs.join('\n\n');
    }
    // ==========================================================================
    // Tool Execution
    // ==========================================================================
    /**
     * Execute tool invocations
     */
    async executeTools(tools, context, toolCalls) {
        if (!this.config.toolExecutor) {
            return 'Tool executor not configured';
        }
        const outputs = [];
        const toolContext = {
            request: context.request,
            ...context.config,
        };
        for (const tool of tools) {
            this.emit('tool:start', tool.name);
            const args = this.interpolateArgs(tool.args || {}, toolContext);
            const result = await this.config.toolExecutor(tool.name, args);
            toolCalls.push({ tool: tool.name, args, result });
            toolContext[tool.name] = result;
            const desc = tool.description ? ` (${tool.description})` : '';
            outputs.push(`${tool.name}${desc}: ${JSON.stringify(result)}`);
            this.emit('tool:complete', tool.name, result);
        }
        return outputs.join('\n');
    }
    // ==========================================================================
    // Code Execution
    // ==========================================================================
    /**
     * Execute code blocks
     */
    async executeCodeBlocks(blocks, context) {
        if (!this.config.codeExecutor) {
            return 'Code executor not configured';
        }
        const outputs = [];
        const codeContext = {
            request: context.request,
            ...context.config,
        };
        for (const block of blocks) {
            this.emit('code:start', block.label || block.language);
            const result = await this.config.codeExecutor(block.code, block.language, codeContext);
            codeContext[block.label || `block_${blocks.indexOf(block)}`] = result;
            const label = block.label ? `[${block.label}]` : `[${block.language}]`;
            outputs.push(`${label}:\n${JSON.stringify(result)}`);
            this.emit('code:complete', block.label || block.language, result);
        }
        return outputs.join('\n\n');
    }
    // ==========================================================================
    // Utilities
    // ==========================================================================
    /**
     * Format skill as guidance text
     */
    formatSkillGuidance(skill, context) {
        const lines = [
            `# ${skill.metadata.name}`,
            '',
            skill.metadata.description,
            '',
        ];
        if (skill.content.usage) {
            lines.push('## When to Use', '', skill.content.usage, '');
        }
        if (skill.content.description) {
            lines.push('## Instructions', '', skill.content.description, '');
        }
        if (skill.content.examples && skill.content.examples.length > 0) {
            lines.push('## Examples', '');
            for (const example of skill.content.examples) {
                lines.push(`- "${example.request}"`);
                if (example.response) {
                    lines.push(`  → ${example.response}`);
                }
            }
            lines.push('');
        }
        lines.push(`User request: "${context.request}"`);
        return lines.join('\n');
    }
    /**
     * Interpolate template variables in arguments
     */
    interpolateArgs(args, context) {
        const result = {};
        for (const [key, value] of Object.entries(args)) {
            if (typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}')) {
                const varName = value.slice(2, -2).trim();
                result[key] = this.resolveVariable(varName, context);
            }
            else if (typeof value === 'string') {
                result[key] = value.replace(/\{\{([^}]+)\}\}/g, (_, varName) => {
                    const resolved = this.resolveVariable(varName.trim(), context);
                    return String(resolved);
                });
            }
            else {
                result[key] = value;
            }
        }
        return result;
    }
    /**
     * Resolve a variable from context
     */
    resolveVariable(name, context) {
        const parts = name.split('.');
        let value = context;
        for (const part of parts) {
            if (value && typeof value === 'object' && part in value) {
                value = value[part];
            }
            else {
                return undefined;
            }
        }
        return value;
    }
    /**
     * Evaluate a condition string
     */
    evaluateCondition(condition, context) {
        try {
            // Simple variable presence check
            if (condition.startsWith('has:')) {
                const varName = condition.slice(4);
                return this.resolveVariable(varName, context) !== undefined;
            }
            // Simple comparison: "varName == value"
            const compMatch = condition.match(/^(\w+)\s*(==|!=|>|<|>=|<=)\s*(.+)$/);
            if (compMatch) {
                const [, varName, op, valueStr] = compMatch;
                const varValue = this.resolveVariable(varName, context);
                const compareValue = this.parseConditionValue(valueStr);
                switch (op) {
                    case '==': return varValue === compareValue;
                    case '!=': return varValue !== compareValue;
                    case '>': return Number(varValue) > Number(compareValue);
                    case '<': return Number(varValue) < Number(compareValue);
                    case '>=': return Number(varValue) >= Number(compareValue);
                    case '<=': return Number(varValue) <= Number(compareValue);
                }
            }
            // Default: truthy check
            return !!this.resolveVariable(condition, context);
        }
        catch {
            return false;
        }
    }
    /**
     * Parse a condition value
     */
    parseConditionValue(value) {
        const trimmed = value.trim();
        if (trimmed === 'true')
            return true;
        if (trimmed === 'false')
            return false;
        if (trimmed === 'null')
            return null;
        if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
            (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
            return trimmed.slice(1, -1);
        }
        const num = Number(trimmed);
        if (!isNaN(num))
            return num;
        return trimmed;
    }
    /**
     * Configure tool executor
     */
    setToolExecutor(executor) {
        this.config.toolExecutor = executor;
    }
    /**
     * Configure code executor
     */
    setCodeExecutor(executor) {
        this.config.codeExecutor = executor;
    }
}
// ============================================================================
// Singleton
// ============================================================================
let executorInstance = null;
export function getSkillExecutor(config) {
    if (!executorInstance) {
        executorInstance = new SkillExecutor(config);
    }
    return executorInstance;
}
export function resetSkillExecutor() {
    executorInstance = null;
}
export default SkillExecutor;
//# sourceMappingURL=executor.js.map