import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject, Observable, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import * as i0 from "@angular/core";
export class FlowExecutorService {
    constructor() {
        this.executionStatus$ = new BehaviorSubject('idle');
        this.currentStepId$ = new BehaviorSubject(null);
        this.executionLogs$ = new BehaviorSubject([]);
        this.executionContext$ = new BehaviorSubject({
            variables: {},
            results: {}
        });
        this.stopExecution$ = new Subject();
        this.pauseExecution$ = new BehaviorSubject(false);
        this.executionStatus = this.executionStatus$.asObservable();
        this.currentStepId = this.currentStepId$.asObservable();
        this.executionLogs = this.executionLogs$.asObservable();
        this.executionContext = this.executionContext$.asObservable();
    }
    async executeFlow(steps, connections) {
        if (this.executionStatus$.value === 'running') {
            console.warn('Execution already in progress');
            return;
        }
        this.resetExecution();
        this.executionStatus$.next('running');
        try {
            const executionOrder = this.buildExecutionOrder(steps, connections);
            this.addLog(null, 'Flow', 'start', `Starting flow execution with ${executionOrder.length} steps`);
            for (const step of executionOrder) {
                if (this.executionStatus$.value === 'paused' || this.executionStatus$.value === 'idle' || this.executionStatus$.value === 'completed' || this.executionStatus$.value === 'error') {
                    break;
                }
                await this.waitForUnpause();
                if (this.executionStatus$.value === 'paused' || this.executionStatus$.value === 'idle' || this.executionStatus$.value === 'completed' || this.executionStatus$.value === 'error') {
                    break;
                }
                await this.executeStep(step, connections);
            }
            if (this.executionStatus$.value !== 'idle' && this.executionStatus$.value !== 'error' && this.executionStatus$.value !== 'completed' && this.executionStatus$.value !== 'paused') {
                this.executionStatus$.next('completed');
                this.addLog(null, 'Flow', 'complete', 'Flow execution completed successfully');
            }
        }
        catch (error) {
            this.executionStatus$.next('error');
            this.addLog(null, 'Flow', 'error', `Flow execution failed: ${error}`);
        }
        finally {
            this.currentStepId$.next(null);
        }
    }
    async executeStep(step, connections) {
        this.currentStepId$.next(step.id);
        this.addLog(step.id, step.name, 'start', `Executing ${step.type} step`);
        try {
            // Simulate execution delay
            await this.delay(1000);
            // Execute based on step type
            const result = await this.executeStepByType(step);
            // Store result in context
            const context = this.executionContext$.value;
            context.results[step.id] = result;
            this.executionContext$.next(context);
            this.addLog(step.id, step.name, 'complete', 'Step completed successfully', result.output);
            // Check conditions for next steps
            if (!result.success) {
                const errorConnections = connections.filter(c => c.source === step.id && c.condition?.expression === 'error');
                if (errorConnections.length > 0) {
                    this.addLog(step.id, step.name, 'log', 'Taking error path');
                }
            }
        }
        catch (error) {
            this.addLog(step.id, step.name, 'error', `Step execution failed: ${error}`);
            throw error;
        }
    }
    async executeStepByType(step) {
        const baseResult = {
            stepId: step.id,
            success: true,
            output: {}
        };
        switch (step.type) {
            case 'prompt':
                return this.executePromptStep(step, baseResult);
            case 'agent':
                return this.executeAgentStep(step, baseResult);
            case 'action':
                return this.executeActionStep(step, baseResult);
            default:
                throw new Error(`Unknown step type: ${step.type}`);
        }
    }
    async executePromptStep(step, baseResult) {
        const promptType = step.selectedOption || 'system';
        const properties = step.propertyValues || {};
        await this.delay(500); // Simulate API call
        const mockResponses = {
            'system': 'System initialized with instructions',
            'user': 'User query processed successfully',
            'assistant': 'Assistant response generated'
        };
        return {
            ...baseResult,
            output: {
                type: promptType,
                message: properties['systemMessage'] || properties['userMessage'] || properties['assistantMessage'] || mockResponses[promptType],
                response: `Mock response for ${promptType} prompt`,
                tokens: Math.floor(Math.random() * 500) + 100
            }
        };
    }
    async executeAgentStep(step, baseResult) {
        const agentType = step.selectedOption || 'research';
        const properties = step.propertyValues || {};
        await this.delay(2000); // Simulate longer processing
        const mockResults = {
            'research': {
                sources: ['Wikipedia', 'ArXiv', 'Google Scholar'],
                findings: ['Finding 1', 'Finding 2', 'Finding 3'],
                summary: 'Research completed with 3 relevant sources'
            },
            'code': {
                language: properties['language'] || 'python',
                code: `# Generated code\ndef example():\n    return "Hello from ${agentType} agent"`,
                explanation: 'Code generated successfully'
            },
            'analysis': {
                type: properties['analysisType'] || 'statistical',
                results: { score: 0.85, confidence: 0.92 },
                summary: 'Analysis completed with high confidence'
            }
        };
        return {
            ...baseResult,
            output: mockResults[agentType] || { message: `${agentType} agent completed` }
        };
    }
    async executeActionStep(step, baseResult) {
        const actionType = step.selectedOption || 'api';
        const properties = step.propertyValues || {};
        await this.delay(1500); // Simulate action execution
        const mockResults = {
            'api': {
                endpoint: properties['endpoint'] || 'https://api.example.com',
                method: properties['method'] || 'GET',
                status: 200,
                response: { data: 'API call successful' }
            },
            'database': {
                query: properties['query'] || 'SELECT * FROM users',
                rows: 42,
                executionTime: '125ms'
            },
            'file': {
                operation: properties['operation'] || 'read',
                path: properties['filePath'] || '/tmp/example.txt',
                success: true,
                size: '2.5KB'
            }
        };
        return {
            ...baseResult,
            output: mockResults[actionType] || { message: `${actionType} action completed` }
        };
    }
    buildExecutionOrder(steps, connections) {
        // Simple topological sort for execution order
        const visited = new Set();
        const order = [];
        // Find starting nodes (no incoming connections)
        const incomingCount = new Map();
        steps.forEach(step => incomingCount.set(step.id, 0));
        connections.forEach(conn => {
            incomingCount.set(conn.target, (incomingCount.get(conn.target) || 0) + 1);
        });
        const startNodes = steps.filter(step => incomingCount.get(step.id) === 0);
        // DFS from each start node
        const visit = (step) => {
            if (visited.has(step.id))
                return;
            visited.add(step.id);
            order.push(step);
            // Find connected steps
            const nextConnections = connections.filter(c => c.source === step.id);
            nextConnections.forEach(conn => {
                const nextStep = steps.find(s => s.id === conn.target);
                if (nextStep)
                    visit(nextStep);
            });
        };
        startNodes.forEach(visit);
        // Add any unvisited steps (disconnected nodes)
        steps.forEach(step => {
            if (!visited.has(step.id)) {
                order.push(step);
            }
        });
        return order;
    }
    pause() {
        if (this.executionStatus$.value === 'running') {
            this.executionStatus$.next('paused');
            this.pauseExecution$.next(true);
            this.addLog(null, 'Flow', 'log', 'Execution paused');
        }
    }
    resume() {
        if (this.executionStatus$.value === 'paused') {
            this.executionStatus$.next('running');
            this.pauseExecution$.next(false);
            this.addLog(null, 'Flow', 'log', 'Execution resumed');
        }
    }
    stop() {
        const currentStatus = this.executionStatus$.value;
        if (currentStatus === 'running' || currentStatus === 'paused') {
            this.executionStatus$.next('idle');
            this.stopExecution$.next();
            this.currentStepId$.next(null);
            this.addLog(null, 'Flow', 'log', 'Execution stopped by user');
        }
    }
    async waitForUnpause() {
        return new Promise((resolve) => {
            if (!this.pauseExecution$.value) {
                resolve();
                return;
            }
            const sub = this.pauseExecution$.subscribe(isPaused => {
                if (!isPaused) {
                    sub.unsubscribe();
                    resolve();
                }
            });
        });
    }
    resetExecution() {
        this.executionLogs$.next([]);
        this.executionContext$.next({
            variables: {},
            results: {}
        });
        this.currentStepId$.next(null);
        this.pauseExecution$.next(false);
    }
    addLog(stepId, stepName, type, message, data) {
        const logs = this.executionLogs$.value;
        logs.push({
            timestamp: new Date(),
            stepId: stepId || 0,
            stepName,
            type,
            message,
            data
        });
        this.executionLogs$.next(logs);
    }
    delay(ms) {
        return new Promise((resolve) => {
            const timer = interval(ms).pipe(takeUntil(this.stopExecution$)).subscribe(() => {
                timer.unsubscribe();
                resolve();
            });
        });
    }
    getExecutionSummary() {
        return new Observable(observer => {
            const logs = this.executionLogs$.value;
            const startLog = logs.find(l => l.type === 'start' && l.stepName === 'Flow');
            const endLog = logs.find(l => (l.type === 'complete' || l.type === 'error') && l.stepName === 'Flow');
            const summary = {
                totalSteps: logs.filter(l => l.type === 'start' && l.stepName !== 'Flow').length,
                completedSteps: logs.filter(l => l.type === 'complete' && l.stepName !== 'Flow').length,
                errors: logs.filter(l => l.type === 'error').length,
                duration: startLog && endLog ? endLog.timestamp.getTime() - startLog.timestamp.getTime() : 0
            };
            observer.next(summary);
            observer.complete();
        });
    }
    static { this.ɵfac = function FlowExecutorService_Factory(t) { return new (t || FlowExecutorService)(); }; }
    static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: FlowExecutorService, factory: FlowExecutorService.ɵfac, providedIn: 'root' }); }
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FlowExecutorService, [{
        type: Injectable,
        args: [{
                providedIn: 'root'
            }]
    }], () => [], null); })();
//# sourceMappingURL=flow-executor.service.js.map