import { PeerCrdtState } from "./peer-crdt-state.js";
import { PeerImageCache } from "./peer-image-cache.js";
export class PeerAppState {
    welcome;
    crdt = new PeerCrdtState();
    view;
    selectedAgent;
    crdtLive;
    localSelectedAgent;
    localLive;
    imageCache = new PeerImageCache();
    peers = [];
    listeners = new Set();
    scheduledEmit;
    subscribe(listener) {
        this.listeners.add(listener);
        return () => {
            this.listeners.delete(listener);
        };
    }
    applyWelcome(welcome) {
        this.cancelScheduledEmit();
        this.crdt.reset();
        this.welcome = welcome;
        this.view = undefined;
        this.selectedAgent = undefined;
        this.crdtLive = undefined;
        this.localSelectedAgent = undefined;
        this.localLive = undefined;
        this.peers = [];
        this.emit();
    }
    resetCrdtSyncState() {
        this.crdt.reset();
    }
    /** For socket layer: collect `imageId`s referenced in payload but not cached yet. */
    collectMissingImageIds(value) {
        return this.imageCache.collectMissingImageIds(value);
    }
    getImageCache() {
        return this.imageCache;
    }
    applyImagePayload(payload) {
        this.imageCache.store(payload);
        this.emit();
    }
    applyCrdtSyncMessage(message, format) {
        const result = this.crdt.applySyncMessage(message, format);
        const view = result.view;
        const selectedAgent = selectCrdtAgent(view, this.welcome?.agentId);
        const projectedLive = projectCrdtLive(view, this.welcome?.agentId);
        const missingImageIds = this.imageCache.collectMissingImageIds({
            agent: selectedAgent,
            live: projectedLive,
            peers: view.peers,
        });
        this.view = view;
        this.selectedAgent = selectedAgent;
        this.crdtLive = projectedLive;
        if (Array.isArray(view.peers)) {
            this.peers = [...view.peers];
        }
        this.scheduleEmit();
        return { missingImageIds };
    }
    getSnapshot() {
        return {
            welcome: this.welcome,
            view: this.view,
            selectedAgent: this.localSelectedAgent ?? this.selectedAgent,
            live: this.imageCache.hydrate(this.localLive ?? this.crdtLive ?? { toolExecutions: [] }),
            peers: [...this.peers],
        };
    }
    applyLocalAgentProjection(agent, live) {
        this.localSelectedAgent = agent;
        this.localLive = live;
        this.emit();
    }
    isReady() {
        return this.welcome !== undefined && this.selectedAgent !== undefined;
    }
    emit() {
        const snapshot = this.getSnapshot();
        for (const listener of this.listeners) {
            listener(snapshot);
        }
    }
    scheduleEmit() {
        if (this.scheduledEmit) {
            return;
        }
        this.scheduledEmit = setImmediate(() => {
            this.scheduledEmit = undefined;
            this.emit();
        });
        this.scheduledEmit.unref?.();
    }
    cancelScheduledEmit() {
        if (!this.scheduledEmit) {
            return;
        }
        clearImmediate(this.scheduledEmit);
        this.scheduledEmit = undefined;
    }
}
function projectCrdtLive(view, preferredAgentId) {
    const agent = selectCrdtAgent(view, preferredAgentId);
    if (!agent) {
        return undefined;
    }
    const streamingMessage = agent.live.streamingMessageIndex === undefined ? undefined : agent.items[agent.live.streamingMessageIndex];
    return {
        streamingMessageId: agent.live.streamingMessageId,
        streamingMessageIndex: agent.live.streamingMessageIndex,
        streamingMessage: streamingMessage?.type === "message" && streamingMessage.message.role === "assistant"
            ? streamingMessage.message
            : undefined,
        toolExecutions: agent.live.toolOrder
            .map((toolCallId) => agent.live.toolsById[toolCallId])
            .filter((tool) => tool !== undefined)
            .map((tool) => ({
            toolCallId: tool.toolCallId,
            toolName: tool.toolName,
            args: tool.args,
            partialResult: tool.partialResult,
            result: tool.result,
            isError: tool.isError,
        })),
        statusMessage: agent.live.statusMessage,
    };
}
function selectCrdtAgent(view, preferredAgentId) {
    if (!view.agentsById || !Array.isArray(view.agentOrder)) {
        return undefined;
    }
    if (preferredAgentId) {
        const preferred = view.agentsById[preferredAgentId];
        if (preferred) {
            return preferred;
        }
    }
    const firstAgentId = view.agentOrder[0];
    return firstAgentId ? view.agentsById[firstAgentId] : undefined;
}
//# sourceMappingURL=peer-app-state.js.map