/**
 * Agent gRPC Client for Clawx
 *
 * Client for agents to connect to the gRPC server
 */
import * as grpc from '@grpc/grpc-js';
import { EventEmitter } from 'events';
import { log } from '../../utils/logger.js';
import { v4 as uuidv4 } from 'uuid';
export class AgentGrpcClient extends EventEmitter {
    options;
    client;
    call = null;
    connected = false;
    registered = false;
    heartbeatTimer = null;
    reconnectTimer = null;
    constructor(options) {
        super();
        this.options = options;
        this.client = new grpc.Client(options.serverAddress, grpc.credentials.createInsecure());
        this.connect();
    }
    connect() {
        try {
            log.info(`[gRPC Client] ${this.options.agentId} connecting to ${this.options.serverAddress}...`);
            this.call = this.client.makeBidiStreamRequest('/AgentService/Connect', (msg) => Buffer.from(JSON.stringify(msg)), (data) => {
                if (data instanceof Buffer)
                    return JSON.parse(data.toString());
                return data;
            }, new grpc.Metadata(), { deadline: Date.now() + 10000 });
            if (!this.call) {
                throw new Error('Failed to create gRPC stream');
            }
            this.setupCallHandlers();
            this.connected = true;
            this.emit('connected');
            // Register with server
            this.register();
        }
        catch (error) {
            this.handleError(error);
        }
    }
    setupCallHandlers() {
        if (!this.call)
            return;
        this.call.on('data', (data) => {
            try {
                const msg = data instanceof Buffer ? JSON.parse(data.toString()) : data;
                this.handleMessage(msg);
            }
            catch (error) {
                log.error('[gRPC Client] Failed to parse message:', error);
            }
        });
        this.call.on('error', (error) => {
            this.handleError(error);
        });
        this.call.on('end', () => {
            this.handleDisconnect();
        });
    }
    handleMessage(msg) {
        this.emit('message', msg);
        switch (msg.type) {
            case 'system':
                this.handleSystemMessage(msg);
                break;
            case 'chat':
                this.emit('chat', {
                    from: msg.from,
                    message: msg.content,
                    timestamp: msg.timestamp,
                });
                break;
            case 'error':
                this.emit('error', new Error(`Server error: ${msg.content}`));
                break;
        }
    }
    handleSystemMessage(msg) {
        try {
            const data = JSON.parse(msg.content);
            if (data.status === 'registered') {
                this.registered = true;
                this.emit('registered', this.options.agentId);
                this.startHeartbeat();
                log.info(`[gRPC Client] ${this.options.agentId} registered successfully`);
            }
        }
        catch (error) {
            // Not a JSON message, just log it
            log.debug(`[gRPC Client] System message: ${msg.content}`);
        }
    }
    register() {
        const msg = {
            id: uuidv4(),
            type: 'register',
            from: this.options.agentId,
            to: 'server',
            content: JSON.stringify({
                agentId: this.options.agentId,
                agentName: this.options.agentName || this.options.agentId,
                persona: this.options.persona,
                timestamp: Date.now(),
            }),
            timestamp: Date.now(),
        };
        this.send(msg);
    }
    startHeartbeat() {
        const interval = this.options.heartbeatInterval || 30000;
        this.heartbeatTimer = setInterval(() => {
            if (this.connected && this.registered) {
                this.sendHeartbeat();
            }
        }, interval);
    }
    sendHeartbeat() {
        const msg = {
            id: uuidv4(),
            type: 'heartbeat',
            from: this.options.agentId,
            to: 'server',
            content: '',
            timestamp: Date.now(),
        };
        this.send(msg);
    }
    handleError(error) {
        log.error(`[gRPC Client] ${this.options.agentId} error:`, error.message);
        this.emit('error', error);
        this.handleDisconnect();
    }
    handleDisconnect() {
        if (!this.connected)
            return;
        this.connected = false;
        this.registered = false;
        this.call = null;
        this.stopHeartbeat();
        this.emit('disconnected');
        // Schedule reconnection
        this.scheduleReconnect();
    }
    scheduleReconnect() {
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
        }
        const delay = this.options.reconnectDelay || 5000;
        this.reconnectTimer = setTimeout(() => {
            log.info(`[gRPC Client] ${this.options.agentId} attempting reconnect...`);
            this.connect();
        }, delay);
    }
    stopHeartbeat() {
        if (this.heartbeatTimer) {
            clearInterval(this.heartbeatTimer);
            this.heartbeatTimer = null;
        }
    }
    // Public API
    send(msg) {
        if (!this.connected || !this.call) {
            log.warn(`[gRPC Client] ${this.options.agentId} not connected, cannot send`);
            return false;
        }
        try {
            this.call.write(msg);
            return true;
        }
        catch (error) {
            log.error(`[gRPC Client] ${this.options.agentId} failed to send:`, error);
            return false;
        }
    }
    sendChat(toAgent, message) {
        const msg = {
            id: uuidv4(),
            type: 'chat',
            from: this.options.agentId,
            to: toAgent,
            content: message,
            timestamp: Date.now(),
        };
        return this.send(msg);
    }
    disconnect() {
        this.connected = false;
        this.registered = false;
        this.stopHeartbeat();
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
            this.reconnectTimer = null;
        }
        if (this.call) {
            this.call.end();
            this.call = null;
        }
        this.client.close();
        log.info(`[gRPC Client] ${this.options.agentId} disconnected`);
        this.emit('disconnected');
    }
    isConnected() {
        return this.connected;
    }
    isRegistered() {
        return this.registered;
    }
    getAgentId() {
        return this.options.agentId;
    }
    getAgentName() {
        return this.options.agentName || this.options.agentId;
    }
    getPersona() {
        return this.options.persona;
    }
}
//# sourceMappingURL=agent-grpc-client.js.map