"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExecutionManager = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const core_1 = require("@vsaf/core");
class ExecutionManager {
    projectPath;
    sseManager;
    activeRuns = new Set();
    constructor(projectPath, sseManager) {
        this.projectPath = projectPath;
        this.sseManager = sseManager;
    }
    async startRun(workflowName, args) {
        const config = (0, core_1.loadConfig)(this.projectPath);
        const workflowPath = (0, path_1.join)(this.projectPath, '.vsaf', 'workflows', `${workflowName}.yaml`);
        if (!(0, fs_1.existsSync)(workflowPath)) {
            throw new Error(`Workflow not found: "${workflowName}"`);
        }
        const yamlContent = (0, fs_1.readFileSync)(workflowPath, 'utf-8');
        const graph = (0, core_1.parseWorkflow)(yamlContent);
        const wsManager = new core_1.WorkspaceManager({ exclude: config.isolation.exclude });
        const ws = await wsManager.create(this.projectPath, workflowName);
        const storeDir = (0, path_1.join)(this.projectPath, '.vsaf', 'storage');
        const runStore = new core_1.RunStore(storeDir);
        const run = runStore.create(workflowName, args, ws.sourcePath, ws.artifactsPath, this.projectPath, yamlContent, ws.branchName);
        this.activeRuns.add(run.runId);
        try {
            const callbacks = {
                onNodeStart: (nodeId) => {
                    runStore.updateNode(run.runId, nodeId, { status: 'running', startedAt: new Date().toISOString() });
                    runStore.save(run.runId);
                    this.sseManager.pushEvent(run.runId, 'node:start', { nodeId, timestamp: new Date().toISOString() });
                },
                onNodeComplete: (nodeId, result) => {
                    runStore.updateNode(run.runId, nodeId, result);
                    runStore.save(run.runId);
                    this.sseManager.pushEvent(run.runId, 'node:complete', {
                        nodeId,
                        durationMs: result.durationMs,
                        output: result.output?.substring(0, 200),
                    });
                },
                onNodeSkip: (nodeId, reason) => {
                    runStore.updateNode(run.runId, nodeId, { status: 'skipped' });
                    runStore.save(run.runId);
                    this.sseManager.pushEvent(run.runId, 'node:skip', { nodeId, reason });
                },
                onNodeFail: (nodeId, error, retryCount) => {
                    runStore.updateNode(run.runId, nodeId, { status: 'failed', error }, { retryCount });
                    runStore.save(run.runId);
                    this.sseManager.pushEvent(run.runId, 'node:fail', { nodeId, error });
                },
                onNodeReset: (nodeId) => {
                    runStore.resetNode(run.runId, nodeId);
                },
            };
            const executor = new core_1.DAGExecutor(ws.sourcePath, this.projectPath, graph, callbacks);
            const step = await executor.step({ args, artifactsDir: ws.artifactsPath });
            if (step.type === 'done') {
                const diff = await wsManager.diff(ws.id);
                if (diff.modified.length > 0 || diff.added.length > 0) {
                    await wsManager.applyChanges(ws.id);
                }
                runStore.complete(run.runId);
                await wsManager.destroy(ws.id);
                this.sseManager.pushEvent(run.runId, 'run:complete', { success: true });
            }
            else if (step.type === 'failed') {
                runStore.fail(run.runId);
                await wsManager.destroy(ws.id);
                this.sseManager.pushEvent(run.runId, 'run:complete', { success: false, error: step.error });
            }
            else if (step.type === 'needs_ai') {
                // HTTP server has no next endpoint — AI workflows cannot proceed
                runStore.fail(run.runId);
                await wsManager.destroy(ws.id);
                const error = 'AI node workflows require the MCP server (vsaf mcp-server). HTTP server only supports bash-only workflows.';
                this.sseManager.pushEvent(run.runId, 'run:complete', { success: false, error });
                return { runId: run.runId, step: { type: 'failed', error, results: step.results } };
            }
            return { runId: run.runId, step };
        }
        catch (err) {
            try {
                await wsManager.destroy(ws.id);
            }
            catch { /* best-effort */ }
            try {
                runStore.fail(run.runId);
            }
            catch { /* best-effort */ }
            throw err;
        }
        finally {
            this.activeRuns.delete(run.runId);
        }
    }
    isActive(runId) {
        return this.activeRuns.has(runId);
    }
}
exports.ExecutionManager = ExecutionManager;
//# sourceMappingURL=execution-manager.js.map