/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { debugLogger } from '../../utils/debugLogger.js';
import { InboxSnapshotImpl } from './inbox.js';
import { ContextWorkingBufferImpl } from './contextWorkingBuffer.js';
export class PipelineOrchestrator {
    pipelines;
    asyncPipelines;
    env;
    tracer;
    activeTimers = [];
    pendingPipelines = new Map();
    pipelineMutex = new Map();
    pipelineScheduled = new Set();
    nodeProvider;
    constructor(pipelines, asyncPipelines, env, tracer) {
        this.pipelines = pipelines;
        this.asyncPipelines = asyncPipelines;
        this.env = env;
        this.tracer = tracer;
        // Background timers not fully implemented in V1 yet
    }
    /**
     * Sets the provider for the latest live nodes.
     * This is used by sequential pipeline runs to ensure they operate on current state.
     */
    setNodeProvider(provider) {
        this.nodeProvider = provider;
    }
    /**
     * Returns a promise that resolves when all currently executing async pipelines have finished.
     * This acts as a 'Pressure Barrier' for the ContextManager.
     */
    async waitForPipelines() {
        const pending = Array.from(this.pendingPipelines.values());
        if (pending.length > 0) {
            debugLogger.log(`[PipelineOrchestrator] Waiting for ${pending.length} pending async pipelines to complete...`);
            await Promise.allSettled(pending);
        }
    }
    isNodeAllowed(node, triggerTargets, protectedTurnIds = new Set()) {
        return (triggerTargets.has(node.id) &&
            !protectedTurnIds.has(node.id) &&
            !protectedTurnIds.has(node.turnId));
    }
    async executeTriggerSync(trigger, buffer, triggerTargets, protectedTurnIds = new Set()) {
        this.tracer.logEvent('Orchestrator', 'Strategy Intent', {
            trigger,
            totalNodes: buffer.nodes.length,
            targetNodes: triggerTargets.size,
        });
        // First, run any sync pipelines matching this trigger
        let currentBuffer = buffer;
        const triggerPipelines = this.pipelines.filter((p) => p.triggers.includes(trigger));
        // Freeze the inbox for this pipeline run
        const inboxSnapshot = new InboxSnapshotImpl(this.env.inbox.getMessages() || []);
        for (const pipeline of triggerPipelines) {
            for (const processor of pipeline.processors) {
                try {
                    this.tracer.logEvent('Orchestrator', `Executing processor synchronously: ${processor.id}`, { nodeCountBefore: currentBuffer.nodes.length });
                    const allowedTargets = currentBuffer.nodes.filter((n) => this.isNodeAllowed(n, triggerTargets, protectedTurnIds));
                    const returnedNodes = await processor.process({
                        buffer: currentBuffer,
                        targets: allowedTargets,
                        inbox: inboxSnapshot,
                    });
                    currentBuffer = currentBuffer.applyProcessorResult(processor.id, allowedTargets, returnedNodes);
                    const addedNodes = returnedNodes.filter((n) => !allowedTargets.some((at) => at.id === n.id));
                    const removedNodes = allowedTargets.filter((at) => !returnedNodes.some((n) => n.id === at.id));
                    this.tracer.logEvent('Orchestrator', 'Transformation Lineage', {
                        processorId: processor.id,
                        inputNodeCount: allowedTargets.length,
                        outputNodeCount: returnedNodes.length,
                        removedNodeIds: removedNodes.map((n) => n.id),
                        addedNodes: addedNodes.map((n) => ({
                            id: n.id,
                            replacesId: n.replacesId,
                            abstractsIds: n.abstractsIds,
                            approxTokens: this.env.tokenCalculator.calculateConcreteListTokens([n]),
                        })),
                    });
                }
                catch (error) {
                    debugLogger.error(`Synchronous processor ${processor.id} failed:`, error);
                }
            }
        }
        // After sync pipelines finish, trigger any matching async pipelines in the background
        void this.executeTriggerAsync(trigger, currentBuffer.nodes, triggerTargets);
        // Success! Drain consumed messages
        this.env.inbox.drainConsumed(inboxSnapshot.getConsumedIds());
        return currentBuffer;
    }
    async executeTriggerAsync(trigger, nodes, triggerTargets) {
        const asyncPipelines = this.asyncPipelines.filter((p) => p.triggers.includes(trigger));
        for (const pipeline of asyncPipelines) {
            void this.handleAsyncExecution(pipeline, nodes, triggerTargets);
        }
    }
    async handleAsyncExecution(pipeline, nodes, targets) {
        if (this.pipelineScheduled.has(pipeline.name)) {
            return;
        }
        this.pipelineScheduled.add(pipeline.name);
        const existing = this.pipelineMutex.get(pipeline.name) || Promise.resolve();
        const nextPromise = (async () => {
            try {
                await existing;
                this.pipelineScheduled.delete(pipeline.name);
                const latestNodes = this.nodeProvider ? this.nodeProvider() : nodes;
                const latestTargets = latestNodes.filter((n) => targets.has(n.id));
                if (latestTargets.length === 0)
                    return;
                debugLogger.log(`[Orchestrator] Executing async pipeline ${pipeline.name}`);
                const inboxSnapshot = new InboxSnapshotImpl(this.env.inbox.getMessages() || []);
                for (const processor of pipeline.processors) {
                    await processor.process({
                        targets: latestTargets,
                        inbox: inboxSnapshot,
                        buffer: ContextWorkingBufferImpl.initialize(latestNodes),
                    });
                }
                this.env.inbox.drainConsumed(inboxSnapshot.getConsumedIds());
            }
            catch (e) {
                debugLogger.error(`Async pipeline chain ${pipeline.name} failed:`, e);
            }
        })();
        this.pipelineMutex.set(pipeline.name, nextPromise);
        const pipelineId = `${pipeline.name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        this.pendingPipelines.set(pipelineId, nextPromise);
        void nextPromise.finally(() => {
            this.pendingPipelines.delete(pipelineId);
            if (this.pipelineMutex.get(pipeline.name) === nextPromise) {
                this.pipelineMutex.delete(pipeline.name);
            }
        });
    }
    shutdown() {
        for (const timer of this.activeTimers) {
            clearInterval(timer);
        }
    }
}
//# sourceMappingURL=orchestrator.js.map