/**
 * gRPC Streaming Client for Master
 *
 * Subscribes to worker event streams via gRPC and forwards events to TUI.
 * Replaces SSE-based StreamingClient.
 */
import { EventEmitter } from 'events';
import { log } from './logger.js';
export class GrpcStreamClient extends EventEmitter {
    agentId;
    agentName;
    isConnected = false;
    eventHandlers = new Set();
    unsubscribeCallback = null;
    constructor(options) {
        super();
        this.agentId = options.agentId;
        this.agentName = options.agentName;
        if (options.onEvent) {
            this.addEventHandler(options.onEvent);
        }
    }
    /**
     * Add an event handler
     */
    addEventHandler(handler) {
        this.on('event', handler);
        this.eventHandlers.add(handler);
    }
    /**
     * Remove an event handler
     */
    removeEventHandler(handler) {
        this.off('event', handler);
        this.eventHandlers.delete(handler);
    }
    /**
     * Connect to gRPC stream for this agent
     * This should be called by GrpcStreamingManager when subscribing
     */
    connect(unsubscribeCallback) {
        this.isConnected = true;
        this.unsubscribeCallback = unsubscribeCallback;
        log.debug(`[gRPC Stream] Connected to agent ${this.agentName} (${this.agentId})`);
    }
    /**
     * Disconnect from gRPC stream
     */
    disconnect() {
        this.isConnected = false;
        if (this.unsubscribeCallback) {
            this.unsubscribeCallback();
            this.unsubscribeCallback = null;
        }
        log.debug(`[gRPC Stream] Disconnected from agent ${this.agentName} (${this.agentId})`);
    }
    /**
     * Handle incoming gRPC frame and convert to StreamEvent
     */
    handleGrpcFrame(frame) {
        if (!this.isConnected) {
            return;
        }
        // Convert gRPC frame to StreamEvent
        const event = this.convertGrpcFrameToEvent(frame);
        if (event) {
            this.emit('event', event);
        }
    }
    /**
     * Convert gRPC frame to StreamEvent
     */
    convertGrpcFrameToEvent(frame) {
        const { type, fromAgentId, toAgentId, parentOperationId, parentOperationType, payload } = frame;
        // Only handle frames from our target agent
        if (fromAgentId !== this.agentId) {
            return null;
        }
        // Convert based on frame type
        switch (type) {
            case 'agent_message_start':
                return {
                    type: 'agent_message_start',
                    turnId: parentOperationId || 'unknown',
                    persona: payload?.persona,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'agent_message_delta':
                return {
                    type: 'agent_message_delta',
                    turnId: parentOperationId || 'unknown',
                    delta: payload?.delta || '',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'agent_message_end':
                return {
                    type: 'agent_message_end',
                    turnId: parentOperationId || 'unknown',
                    finalMessage: payload?.finalMessage || '',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'task_started':
                return {
                    type: 'task_started',
                    taskId: parentOperationId || 'unknown',
                    tool: payload?.tool,
                    params: payload?.params,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'task_progress':
                return {
                    type: 'task_progress',
                    taskId: parentOperationId || 'unknown',
                    progress: payload?.progress || 0,
                    message: payload?.message,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'tool_started':
                return {
                    type: 'tool_started',
                    taskId: parentOperationId || 'unknown',
                    toolName: payload?.tool,
                    params: payload?.params,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'tool_stdout':
                return {
                    type: 'tool_stdout',
                    taskId: parentOperationId || 'unknown',
                    data: payload?.data || '',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'tool_stderr':
                return {
                    type: 'tool_stderr',
                    taskId: parentOperationId || 'unknown',
                    data: payload?.data || '',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'tool_finished':
                return {
                    type: 'tool_finished',
                    taskId: parentOperationId || 'unknown',
                    result: payload?.result,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'task_completed':
                return {
                    type: 'task_completed',
                    taskId: parentOperationId || 'unknown',
                    result: payload?.result,
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'task_failed':
                return {
                    type: 'task_failed',
                    taskId: parentOperationId || 'unknown',
                    error: payload?.error || 'Unknown error',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            case 'task_cancelled':
                return {
                    type: 'task_cancelled',
                    taskId: parentOperationId || 'unknown',
                    parentOperationId,
                    parentOperationType: parentOperationType,
                };
            default:
                log.debug(`[gRPC Stream] Unhandled frame type: ${type}`);
                return null;
        }
    }
    /**
     * Get connection status
     */
    get connected() {
        return this.isConnected;
    }
}
/**
 * gRPC Streaming Manager
 *
 * Manages multiple gRPC stream clients and routes frames from GrpcServer
 */
export class GrpcStreamingManager extends EventEmitter {
    clients = new Map(); // agentId → client
    frameHandler = null;
    constructor() {
        super();
    }
    /**
     * Subscribe to agent events via gRPC
     */
    subscribeToAgent(agentId, agentName, onEvent) {
        let client = this.clients.get(agentId);
        if (!client) {
            client = new GrpcStreamClient({
                agentId,
                agentName,
                onEvent,
            });
            // Connect client with unsubscribe callback
            client.connect(() => {
                this.clients.delete(agentId);
                log.debug(`[gRPC Stream] Unsubscribed from agent ${agentName} (${agentId})`);
            });
            this.clients.set(agentId, client);
            log.debug(`[gRPC Stream] Subscribed to agent ${agentName} (${agentId})`);
        }
        else {
            // Add event handler to existing client
            client.addEventHandler(onEvent);
        }
        return client;
    }
    /**
     * Unsubscribe from agent events
     */
    unsubscribeFromAgent(agentId, handler) {
        const client = this.clients.get(agentId);
        if (!client) {
            return;
        }
        if (handler) {
            client.removeEventHandler(handler);
            // If no more handlers, disconnect
            if (client.listenerCount('event') === 0) {
                client.disconnect();
                this.clients.delete(agentId);
            }
        }
        else {
            // Remove all handlers and disconnect
            client.disconnect();
            this.clients.delete(agentId);
        }
    }
    /**
     * Handle incoming gRPC frame from GrpcServer
     * This should be called by the master's GrpcServer onFrame callback
     */
    handleGrpcFrame(frame) {
        const { fromAgentId } = frame;
        // Route frame to appropriate client
        const client = this.clients.get(fromAgentId);
        if (client) {
            client.handleGrpcFrame(frame);
        }
    }
    /**
     * Get all connected clients
     */
    getClients() {
        return new Map(this.clients);
    }
    /**
     * Clean up all clients
     */
    cleanup() {
        for (const [agentId, client] of this.clients) {
            client.disconnect();
        }
        this.clients.clear();
    }
}
//# sourceMappingURL=grpc-streaming-client.js.map