/**
 * SignalingClient - WebSocket signaling client
 */

import { EventEmitter } from 'eventemitter3';

export interface SignalingMessage {
    type: 'offer' | 'answer' | 'ice-candidate' | 'join-room' | 'leave-room' | 'user-joined' | 'user-left';
    data?: any;
    roomId?: string;
    userId?: string;
}

export interface SignalingClientEvents {
    'connected': () => void;
    'disconnected': () => void;
    'message': (message: SignalingMessage) => void;
    'error': (error: Error) => void;
}

export class SignalingClient extends EventEmitter<SignalingClientEvents> {
    private ws?: WebSocket;
    private url: string;
    private reconnectAttempts: number = 0;
    private maxReconnectAttempts: number = 5;
    private reconnectDelay: number = 1000;
    private isConnecting: boolean = false;

    constructor(url: string, options?: {
        maxReconnectAttempts?: number;
        reconnectDelay?: number;
    }) {
        super();
        this.url = url;

        if (options?.maxReconnectAttempts) {
            this.maxReconnectAttempts = options.maxReconnectAttempts;
        }

        if (options?.reconnectDelay) {
            this.reconnectDelay = options.reconnectDelay;
        }
    }

    connect(): Promise<void> {
        return new Promise((resolve, reject) => {
            if (this.isConnecting || this.isConnected()) {
                resolve();
                return;
            }

            this.isConnecting = true;
            this.ws = new WebSocket(this.url);

            this.ws.onopen = () => {
                this.isConnecting = false;
                this.reconnectAttempts = 0;
                this.emit('connected');
                resolve();
            };

            this.ws.onclose = () => {
                this.isConnecting = false;
                this.emit('disconnected');
                this.attemptReconnect();
            };

            this.ws.onerror = (event) => {
                this.isConnecting = false;
                const error = new Error('WebSocket connection error');
                this.emit('error', error);
                reject(error);
            };

            this.ws.onmessage = (event) => {
                try {
                    const message: SignalingMessage = JSON.parse(event.data);
                    this.emit('message', message);
                } catch (error) {
                    this.emit('error', new Error('Failed to parse message'));
                }
            };
        });
    }

    private attemptReconnect(): void {
        if (this.reconnectAttempts >= this.maxReconnectAttempts) {
            this.emit('error', new Error('Max reconnection attempts reached'));
            return;
        }

        this.reconnectAttempts++;

        setTimeout(() => {
            this.connect().catch(() => {
                // Reconnection failed, will try again
            });
        }, this.reconnectDelay * this.reconnectAttempts);
    }

    send(message: SignalingMessage): void {
        if (!this.isConnected()) {
            throw new Error('WebSocket is not connected');
        }

        this.ws!.send(JSON.stringify(message));
    }

    joinRoom(roomId: string, userId: string): void {
        this.send({
            type: 'join-room',
            roomId,
            userId
        });
    }

    leaveRoom(roomId: string, userId: string): void {
        this.send({
            type: 'leave-room',
            roomId,
            userId
        });
    }

    sendOffer(offer: RTCSessionDescriptionInit, roomId: string, userId: string): void {
        this.send({
            type: 'offer',
            data: offer,
            roomId,
            userId
        });
    }

    sendAnswer(answer: RTCSessionDescriptionInit, roomId: string, userId: string): void {
        this.send({
            type: 'answer',
            data: answer,
            roomId,
            userId
        });
    }

    sendIceCandidate(candidate: RTCIceCandidate, roomId: string, userId: string): void {
        this.send({
            type: 'ice-candidate',
            data: candidate,
            roomId,
            userId
        });
    }

    isConnected(): boolean {
        return this.ws?.readyState === WebSocket.OPEN;
    }

    disconnect(): void {
        if (this.ws) {
            this.ws.close();
            this.ws = undefined;
        }
        this.removeAllListeners();
    }
} 