/**
 * WebSocket Client para WebTrader
 * Maneja conexiones en tiempo real para actualizaciones de precios y datos
 */

class WebSocketClient {
    constructor(url = null) {
        // Construir URL del WebSocket usando el host actual y el puerto configurado
        const cfg = (typeof window !== 'undefined' ? (window.webtraderConfig || {}) : {});
        const host = (typeof window !== 'undefined' && window.location && window.location.hostname) ? window.location.hostname : 'localhost';
        const proto = (typeof window !== 'undefined' && window.location && window.location.protocol === 'https:' ) ? 'wss' : 'ws';
        const port = Number(cfg.websocket_port) || 8082;
        const finalUrl = url || `${proto}://${host}:${port}`;

        this.url = finalUrl;
        this.mode = (cfg.websocket_mode === 'finage') ? 'finage' : 'local';
        this.ws = null;
        this.connected = false;
        this.authenticated = false;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 0; // 0 = ilimitado
        this.reconnectDelay = 1000;
        this.reconnectMaxDelay = 10000;
        this.subscriptions = new Set();
        this.callbacks = new Map();
        
        // Bind methods
        this.connect = this.connect.bind(this);
        this.onOpen = this.onOpen.bind(this);
        this.onMessage = this.onMessage.bind(this);
        this.onClose = this.onClose.bind(this);
        this.onError = this.onError.bind(this);
        
        this.connect();
    }
    
    /**
     * Conectar al WebSocket
     */
    connect() {
        try {
            console.log('Conectando a WebSocket:', this.url);
            this.ws = new WebSocket(this.url);
            
            this.ws.onopen = this.onOpen;
            this.ws.onmessage = this.onMessage;
            this.ws.onclose = this.onClose;
            this.ws.onerror = this.onError;
            
        } catch (error) {
            console.error('Error conectando WebSocket:', error);
            this.scheduleReconnect();
        }
    }
    
    /**
     * Manejar apertura de conexión
     */
    onOpen(event) {
        console.log('WebSocket conectado');
        this.connected = true;
        this.reconnectAttempts = 0;
        
        // Autenticar automáticamente si no es invitado
        const cfg = (typeof window !== 'undefined' ? (window.webtraderConfig || {}) : {});
        const isGuest = Boolean(cfg.guest);
        if (this.mode === 'finage') {
            const shouldAuto = (cfg.auto_subscribe !== false);
            const hasList = Array.isArray(cfg.default_symbols) && cfg.default_symbols.length > 0;
            if (shouldAuto && hasList) {
                const symbols = cfg.default_symbols.join(',');
                try { this.ws.send(JSON.stringify({ action: 'subscribe', symbols })); } catch (e) {}
            }
            this.emit('connected');
        } else {
            if (!isGuest) {
                this.authenticate();
            } else {
                this.emit('guest');
            }
        }
        
        // Notificar conexión exitosa
        this.emit('connected');
    }
    
    /**
     * Manejar mensajes recibidos
     */
    onMessage(event) {
        try {
            const raw = event.data;
            if (typeof raw === 'string') {
                const t = raw.trim();
                const first = t.charAt(0);
                // Ignorar mensajes no JSON (p. ej., banners HTML, pings de texto)
                if (first !== '{' && first !== '[') {
                    return;
                }
            }
            if (this.mode === 'finage') {
                const lines = String(raw).trim().split('\n');
                for (const line of lines) {
                    if (!line) continue;
                    let m; try { m = JSON.parse(line); } catch (_) { continue; }
                    if (m && m.s) {
                        const payload = {
                            type: 'price_update',
                            symbol: m.s,
                            bid: parseFloat(m.b ?? m.bp ?? 0),
                            ask: parseFloat(m.a ?? m.ap ?? 0),
                            timestamp: parseInt(m.t ?? Date.now()),
                            change: m.dd != null ? parseFloat(m.dd) : null,
                            change_percent: m.dc != null ? parseFloat(m.dc) : null,
                            source: 'finage'
                        };
                        this.emit('price_update', payload);
                    }
                }
                return;
            }
            const data = JSON.parse(raw);
            switch (data.type) {
                case 'welcome':
                    console.log('Bienvenida del servidor:', data.message);
                    break;
                    
                case 'authenticated':
                    this.authenticated = true;
                    console.log('Autenticado como cliente:', data.client_id);
                    this.emit('authenticated', data);
                    this.resubscribeAll();
                    break;
                    
                case 'subscribed':
                    console.log(`Suscrito a ${data.channel}${data.symbol ? ` (${data.symbol})` : ''}`);
                    this.emit('subscribed', data);
                    break;
                    
                case 'price_update':
                    this.emit('price_update', data);
                    break;
                case 'candle_update':
                    this.emit('candle_update', data);
                    break;
                case 'candle_snapshot':
                    this.emit('candle_snapshot', data);
                    break;
                case 'feed_status':
                    this.emit('feed_status', data);
                    break;
                    
                case 'account_update':
                    this.emit('account_update', data);
                    break;
                    
                case 'order_update':
                    this.emit('order_update', data);
                    break;
                    
                case 'position_update':
                    this.emit('position_update', data);
                    break;
                case 'alert':
                    this.emit('alert', data);
                    break;
                    
                case 'pong':
                    this.emit('pong', data);
                    break;
                    
                case 'error':
                    console.error('Error del servidor:', data.message);
                    this.emit('error', data);
                    break;
                    
                default:
                    console.warn('Tipo de mensaje no reconocido:', data.type);
            }
            
        } catch (error) {
            console.error('Error procesando mensaje WebSocket:', error);
        }
    }
    
    /**
     * Manejar cierre de conexión
     */
    onClose(event) {
        console.log('WebSocket desconectado:', event.code, event.reason);
        this.connected = false;
        this.authenticated = false;
        
        this.emit('disconnected', { code: event.code, reason: event.reason });
        
        // Intentar reconectar si no fue cierre intencional
        if (event.code !== 1000) {
            this.scheduleReconnect();
        }
    }
    
    /**
     * Manejar errores
     */
    onError(error) {
        console.error('Error WebSocket:', error);
        this.emit('websocket_error', error);
    }
    
    /**
     * Autenticar con el servidor
     */
    authenticate() {
        if (!this.connected) return;
        
        // Usar client_id de la configuración del WebTrader
        const clientId = (typeof window !== 'undefined' && window.webtraderConfig) ? window.webtraderConfig.client_id : null;
        if (!clientId) {
            console.warn('Sin client_id en configuración; usar modo invitado');
            this.emit('guest');
            return;
        }
        this.send({ type: 'authenticate', client_id: clientId });
    }
    
    /**
     * Suscribirse a un canal
     */
    subscribe(channel, symbol = null, timeframe = null) {
        const cfg = (typeof window !== 'undefined' ? (window.webtraderConfig || {}) : {});
        if (this.mode === 'finage') {
            const subscription = { channel, symbol, timeframe };
            this.subscriptions.add(subscription);
            if (channel === 'prices' && symbol) {
                try { this.ws.send(JSON.stringify({ action: 'subscribe', symbols: symbol })); } catch (e) {}
            }
            return;
        }
        const isGuest = Boolean(cfg.guest);
        const guestAllowed = (channel === 'prices' || channel === 'candle_update');
        if (!this.authenticated && !isGuest) {
            console.warn('No autenticado, guardando suscripción para después');
            this.subscriptions.add({ channel, symbol, timeframe });
            return;
        }
        if (!this.authenticated && isGuest && !guestAllowed) {
            console.warn('Modo invitado: canal no permitido', channel);
            return;
        }
        const subscription = { channel, symbol, timeframe };
        this.subscriptions.add(subscription);
        this.send({ type: 'subscribe', channel, symbol, timeframe });
    }
    
    /**
     * Cancelar suscripción
     */
    unsubscribe(channel, symbol = null, timeframe = null) {
        this.subscriptions.delete({ channel, symbol, timeframe });
        
        if (this.authenticated) {
            this.send({
                type: 'unsubscribe',
                channel: channel,
                symbol: symbol,
                timeframe: timeframe
            });
        }
    }
    
    /**
     * Re-suscribirse a todos los canales después de reconexión
     */
    resubscribeAll() {
        for (const s of this.subscriptions) {
            this.subscribe(s.channel, s.symbol, s.timeframe);
        }
    }
    
    /**
     * Enviar ping al servidor
     */
    ping() {
        if (this.connected && this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.send({ type: 'ping' });
        }
    }
    
    /**
     * Enviar mensaje al servidor
     */
    send(data) {
        if (this.connected && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(data));
        } else {
            console.warn('WebSocket no conectado, no se puede enviar:', data);
        }
    }
    
    /**
     * Programar reconexión
     */
    scheduleReconnect() {
        // maxReconnectAttempts=0 -> ilimitado
        if (this.maxReconnectAttempts > 0 && this.reconnectAttempts >= this.maxReconnectAttempts) {
            console.error('Máximo número de intentos de reconexión alcanzado');
            this.emit('max_reconnect_attempts');
            return;
        }
        this.reconnectAttempts++;
        let delay = this.reconnectDelay * Math.pow(2, Math.max(0, this.reconnectAttempts - 1));
        delay = Math.min(delay, this.reconnectMaxDelay);
        console.log(`Reintentando conexión en ${delay}ms (intento ${this.reconnectAttempts})`);
        setTimeout(() => { this.connect(); }, delay);
    }
    
    /**
     * Registrar callback para eventos
     */
    on(event, callback) {
        if (!this.callbacks.has(event)) {
            this.callbacks.set(event, []);
        }
        this.callbacks.get(event).push(callback);
    }
    
    /**
     * Desregistrar callback
     */
    off(event, callback) {
        if (this.callbacks.has(event)) {
            const callbacks = this.callbacks.get(event);
            const index = callbacks.indexOf(callback);
            if (index > -1) {
                callbacks.splice(index, 1);
            }
        }
    }
    
    /**
     * Emitir evento
     */
    emit(event, data = null) {
        if (this.callbacks.has(event)) {
            this.callbacks.get(event).forEach(callback => {
                try {
                    callback(data);
                } catch (error) {
                    console.error(`Error en callback de ${event}:`, error);
                }
            });
        }
    }
    
    /**
     * Cerrar conexión
     */
    disconnect() {
        if (this.ws) {
            this.ws.close(1000, 'Desconexión intencional');
        }
    }
    
    /**
     * Obtener estado de conexión
     */
    getConnectionState() {
        return {
            connected: this.connected,
            authenticated: this.authenticated,
            reconnectAttempts: this.reconnectAttempts,
            subscriptions: Array.from(this.subscriptions)
        };
    }
}

// Exportar para uso global
window.WebSocketClient = WebSocketClient;
