import { createServer } from 'node:http';
import { WebSocketServer } from 'ws';
export class RemoteControlWebSocketServer {
    runtime;
    host;
    requestedPort;
    httpServer;
    wsServer;
    activeSocket;
    unsubscribeActive;
    constructor(runtime, options) {
        this.runtime = runtime;
        this.host = options.host ?? '127.0.0.1';
        this.requestedPort = options.port;
    }
    get port() {
        const address = this.httpServer?.address();
        if (typeof address === 'object' && address?.port) {
            return address.port;
        }
        return this.requestedPort;
    }
    async start() {
        if (this.httpServer || this.wsServer) {
            throw new Error('Remote control WebSocket server already started');
        }
        this.httpServer = createServer();
        this.wsServer = new WebSocketServer({ server: this.httpServer });
        this.wsServer.on('connection', (socket) => this.handleConnection(socket));
        await new Promise((resolve, reject) => {
            this.httpServer.once('error', reject);
            this.httpServer.listen(this.requestedPort, this.host, () => {
                this.httpServer.off('error', reject);
                resolve();
            });
        });
    }
    async close() {
        this.unsubscribeCurrentSocket();
        this.activeSocket?.close();
        this.activeSocket = undefined;
        await new Promise((resolve) => {
            if (!this.wsServer) {
                resolve();
                return;
            }
            this.wsServer.close(() => resolve());
            this.wsServer = undefined;
        });
        await new Promise((resolve) => {
            if (!this.httpServer) {
                resolve();
                return;
            }
            this.httpServer.close(() => resolve());
            this.httpServer = undefined;
        });
    }
    handleConnection(socket) {
        if (this.activeSocket && this.activeSocket !== socket) {
            this.unsubscribeCurrentSocket();
            this.activeSocket.close(4000, 'controller_replaced');
            this.runtime.detach();
        }
        this.activeSocket = socket;
        this.unsubscribeActive = this.runtime.subscribe((event) => {
            this.send(socket, {
                type: 'event',
                event
            });
        });
        socket.on('message', (raw) => {
            void this.handleMessage(socket, raw);
        });
        socket.once('close', () => {
            if (this.activeSocket === socket) {
                this.unsubscribeCurrentSocket();
                this.activeSocket = undefined;
                this.runtime.detach();
            }
        });
    }
    unsubscribeCurrentSocket() {
        this.unsubscribeActive?.();
        this.unsubscribeActive = undefined;
    }
    async handleMessage(socket, raw) {
        let message;
        try {
            message = JSON.parse(String(raw));
        }
        catch {
            this.send(socket, {
                type: 'error',
                error: 'invalid_json'
            });
            return;
        }
        if (!message.command || typeof message.command.type !== 'string') {
            this.send(socket, {
                type: 'error',
                id: message.id,
                error: 'invalid_command'
            });
            return;
        }
        try {
            const result = await this.runtime.handleCommand(message.command);
            this.send(socket, {
                type: 'response',
                id: message.id,
                result
            });
        }
        catch (error) {
            this.send(socket, {
                type: 'error',
                id: message.id,
                error: error instanceof Error ? error.message : String(error)
            });
        }
    }
    send(socket, message) {
        if (socket.readyState !== socket.OPEN) {
            return;
        }
        socket.send(JSON.stringify(message));
    }
}
//# sourceMappingURL=ws-server.js.map