import { EventEmitter } from 'events';
import { ActivityExecutor } from './activity-executor.js';
import { VariableResolver } from './variable-resolver.js';
export class ProcessEngine extends EventEmitter {
    executions = new Map();
    activityExecutor;
    variableResolver;
    store;
    agentOrchestrator;
    constructor(store, agentOrchestrator) {
        super();
        this.store = store;
        this.agentOrchestrator = agentOrchestrator;
        this.activityExecutor = new ActivityExecutor(this, agentOrchestrator);
        this.variableResolver = new VariableResolver();
    }
    async executeProcess(process, triggerId = 'manual', initialVariables = {}) {
        const execution = {
            id: this.generateExecutionId(),
            processId: process.id,
            processVersion: process.version,
            status: 'pending',
            triggeredBy: triggerId,
            startedAt: new Date().toISOString(),
            variables: { ...process.variables, ...initialVariables },
            activityResults: [],
            logs: []
        };
        this.executions.set(execution.id, execution);
        await this.store.saveExecution(execution);
        this.log(execution.id, 'info', `Starting process: ${process.name}`);
        this.emit('execution:started', execution);
        try {
            execution.status = 'running';
            await this.store.saveExecution(execution);
            // Execute activities sequentially
            for (const activity of process.activities) {
                if (execution.status !== 'running') {
                    break; // Process was paused or cancelled
                }
                await this.executeActivity(execution, activity);
            }
            if (execution.status === 'running') {
                execution.status = 'completed';
                execution.completedAt = new Date().toISOString();
                execution.duration = Date.now() - new Date(execution.startedAt).getTime();
                // Execute success handlers if any
                if (process.onSuccess) {
                    for (const activity of process.onSuccess) {
                        await this.executeActivity(execution, activity);
                    }
                }
            }
        }
        catch (error) {
            execution.status = 'failed';
            execution.error = this.createProcessError(error);
            execution.completedAt = new Date().toISOString();
            execution.duration = Date.now() - new Date(execution.startedAt).getTime();
            this.log(execution.id, 'error', `Process failed: ${execution.error.message}`);
            // Execute failure handlers if any
            if (process.onFailure) {
                for (const activity of process.onFailure) {
                    try {
                        await this.executeActivity(execution, activity);
                    }
                    catch (handlerError) {
                        this.log(execution.id, 'error', `Failure handler error: ${handlerError}`);
                    }
                }
            }
        }
        await this.store.saveExecution(execution);
        this.emit('execution:completed', execution);
        return execution;
    }
    async executeActivity(execution, activity) {
        // Check condition if specified
        if (activity.condition) {
            const shouldRun = await this.variableResolver.evaluateCondition(activity.condition, execution.variables);
            if (!shouldRun) {
                const result = {
                    activityId: activity.id,
                    status: 'skipped',
                    startedAt: new Date().toISOString(),
                    completedAt: new Date().toISOString(),
                    duration: 0
                };
                execution.activityResults.push(result);
                this.log(execution.id, 'info', `Skipped activity: ${activity.name} (condition not met)`);
                return result;
            }
        }
        this.log(execution.id, 'info', `Executing activity: ${activity.name}`, activity.id);
        this.emit('activity:started', { execution, activity });
        const startTime = Date.now();
        const result = {
            activityId: activity.id,
            status: 'success',
            startedAt: new Date().toISOString(),
            completedAt: '',
            duration: 0
        };
        try {
            // Resolve input variables
            const resolvedInputs = await this.variableResolver.resolveVariables(activity.inputs || {}, execution.variables);
            // Execute based on activity type
            const outputs = await this.activityExecutor.execute(activity, resolvedInputs, execution);
            result.outputs = outputs;
            result.status = 'success';
            // Store outputs in execution variables
            if (activity.outputs && outputs) {
                for (const outputVar of activity.outputs) {
                    execution.variables[outputVar] = outputs[outputVar];
                }
            }
        }
        catch (error) {
            result.status = 'failed';
            result.error = error instanceof Error ? error.message : String(error);
            // Handle error based on error handler configuration
            if (activity.errorHandler) {
                await this.handleActivityError(execution, activity, error);
            }
            else {
                throw error; // Propagate to process level
            }
        }
        finally {
            result.completedAt = new Date().toISOString();
            result.duration = Date.now() - startTime;
            execution.activityResults.push(result);
            this.emit('activity:completed', { execution, activity, result });
        }
        return result;
    }
    async handleActivityError(execution, activity, error) {
        const handler = activity.errorHandler;
        switch (handler.type) {
            case 'retry':
                // Implement retry logic
                if (handler.retryPolicy) {
                    // TODO: Implement retry with backoff
                    this.log(execution.id, 'warn', `Retrying activity: ${activity.name}`);
                }
                break;
            case 'skip':
                this.log(execution.id, 'warn', `Skipping failed activity: ${activity.name}`);
                break;
            case 'alternate':
                if (handler.alternateActivities) {
                    for (const altActivity of handler.alternateActivities) {
                        await this.executeActivity(execution, altActivity);
                    }
                }
                break;
            case 'fail':
            default:
                throw error;
        }
    }
    async pauseExecution(executionId) {
        const execution = this.executions.get(executionId);
        if (execution && execution.status === 'running') {
            execution.status = 'paused';
            await this.store.saveExecution(execution);
            this.emit('execution:paused', execution);
        }
    }
    async resumeExecution(executionId) {
        const execution = this.executions.get(executionId);
        if (execution && execution.status === 'paused') {
            execution.status = 'running';
            await this.store.saveExecution(execution);
            this.emit('execution:resumed', execution);
            // TODO: Continue execution from where it left off
        }
    }
    async cancelExecution(executionId) {
        const execution = this.executions.get(executionId);
        if (execution && ['running', 'paused', 'waiting'].includes(execution.status)) {
            execution.status = 'cancelled';
            execution.completedAt = new Date().toISOString();
            execution.duration = Date.now() - new Date(execution.startedAt).getTime();
            await this.store.saveExecution(execution);
            this.emit('execution:cancelled', execution);
        }
    }
    getExecution(executionId) {
        return this.executions.get(executionId);
    }
    log(executionId, level, message, activityId) {
        const execution = this.executions.get(executionId);
        if (execution) {
            const entry = {
                timestamp: new Date().toISOString(),
                level,
                message,
                activityId
            };
            execution.logs.push(entry);
            this.emit('execution:log', { executionId, entry });
        }
    }
    createProcessError(error) {
        return {
            message: error instanceof Error ? error.message : String(error),
            code: error.code,
            stack: error instanceof Error ? error.stack : undefined,
            retryable: false // TODO: Determine based on error type
        };
    }
    generateExecutionId() {
        return `exec-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
    }
}
//# sourceMappingURL=process-engine.js.map