/**
 * WebSocket connection manager for Pi Multi-FX Pedal.
 *
 * Establishes a persistent WebSocket connection to the pedal server,
 * auto-reconnects on disconnect, and dispatches typed events to
 * registered handlers.
 *
 * Usage:
 *   const ws = new PedalWebSocket();
 *   ws.on('state', (data) => { ... });
 *   ws.on('preset_changed', (data) => { ... });
 */
class PedalWebSocket {
    constructor(url = null) {
        this.url = url || (() => {
            const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            return `${proto}//${window.location.host}/ws`;
        })();
        this.ws = null;
        this._handlers = {};
        this._reconnectTimer = null;
        this._reconnectDelay = 1000;  // start at 1s, back off
        this._closed = false;
        this.connect();
    }

    connect() {
        if (this._closed) return;
        try {
            this.ws = new WebSocket(this.url);
        } catch (e) {
            console.error('[WS] Connection error:', e);
            this._scheduleReconnect();
            return;
        }

        this.ws.onopen = () => {
            console.log('[WS] Connected');
            this._reconnectDelay = 1000;
            this._updateConnectionStatus(true);
        };

        this.ws.onmessage = (event) => {
            try {
                const msg = JSON.parse(event.data);
                this._dispatch(msg.type, msg);
            } catch (e) {
                console.warn('[WS] Parse error:', e);
            }
        };

        this.ws.onclose = () => {
            console.log('[WS] Disconnected');
            this._updateConnectionStatus(false);
            this._scheduleReconnect();
        };

        this.ws.onerror = (e) => {
            console.error('[WS] Error:', e);
        };
    }

    _scheduleReconnect() {
        if (this._closed) return;
        if (this._reconnectTimer) clearTimeout(this._reconnectTimer);
        this._reconnectTimer = setTimeout(() => {
            console.log(`[WS] Reconnecting (delay=${this._reconnectDelay}ms)...`);
            this._reconnectDelay = Math.min(this._reconnectDelay * 1.5, 15000);
            this.connect();
        }, this._reconnectDelay);
    }

    _updateConnectionStatus(connected) {
        const el = document.getElementById('connection-status');
        if (el) {
            el.textContent = connected ? 'Connected' : 'Disconnected';
            el.classList.toggle('connected', connected);
        }
    }

    on(type, handler) {
        if (!this._handlers[type]) this._handlers[type] = [];
        this._handlers[type].push(handler);
    }

    off(type, handler) {
        if (!this._handlers[type]) return;
        this._handlers[type] = this._handlers[type].filter(h => h !== handler);
    }

    _dispatch(type, msg) {
        const handlers = this._handlers[type];
        if (handlers) {
            for (const h of handlers) h(msg);
        }
        // Also fire wildcard handlers
        const wildcard = this._handlers['*'];
        if (wildcard) {
            for (const h of wildcard) h(msg);
        }
    }

    send(data) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(data));
        }
    }

    close() {
        this._closed = true;
        if (this._reconnectTimer) clearTimeout(this._reconnectTimer);
        if (this.ws) this.ws.close();
    }
}

// Singleton instance — shared across all pages
const pedalWS = new PedalWebSocket();