/**
 * AniWorld - WebSocket Client Module
 *
 * Native WebSocket connection management with Socket.IO-style interface.
 * Uses FastAPI native WebSocket backend with room-based messaging.
 *
 * Dependencies: constants.js
 */

var AniWorld = window.AniWorld || {};

AniWorld.WebSocketClient = (function() {
    'use strict';

    const WS_EVENTS = AniWorld.Constants.WS_EVENTS;

    let ws = null;
    let isConnected = false;
    let eventHandlers = {};
    let rooms = new Set();
    let messageQueue = [];
    let reconnectAttempts = 0;
    const maxReconnectAttempts = 5;
    const reconnectDelay = 1000;
    let autoReconnect = true;

    /**
     * Get WebSocket URL based on current page URL
     */
    function getWebSocketUrl() {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        const host = window.location.host;
        return protocol + '//' + host + '/ws/connect';
    }

    /**
     * Initialize WebSocket connection
     * @param {Object} handlers - Object mapping event names to handler functions
     */
    function init(handlers) {
        handlers = handlers || {};
        eventHandlers = handlers;

        connect();
    }

    /**
     * Connect to WebSocket server
     */
    function connect() {
        if (ws && ws.readyState === WebSocket.OPEN) {
            console.log('WebSocket already connected');
            return;
        }

        try {
            const url = getWebSocketUrl();
            console.log('Connecting to WebSocket:', url);
            ws = new WebSocket(url);

            ws.onopen = function() {
                console.log('WebSocket connected');
                isConnected = true;
                reconnectAttempts = 0;

                // Emit connect event to handlers
                emitToHandlers('connect');

                // Join default rooms for this application
                joinRoom('scan');
                joinRoom('downloads');
                joinRoom('queue');

                // Rejoin any previously joined rooms
                rejoinRooms();

                // Process queued messages
                processMessageQueue();

                // Call custom connect handler if provided
                if (eventHandlers.onConnect) {
                    eventHandlers.onConnect();
                }
            };

            ws.onmessage = function(event) {
                handleMessage(event.data);
            };

            ws.onerror = function(error) {
                console.error('WebSocket error:', error);
                emitToHandlers('error', { error: 'WebSocket connection error' });
            };

            ws.onclose = function(event) {
                console.log('WebSocket disconnected', event.code, event.reason);
                isConnected = false;
                emitToHandlers('disconnect', { code: event.code, reason: event.reason });

                // Call custom disconnect handler if provided
                if (eventHandlers.onDisconnect) {
                    eventHandlers.onDisconnect();
                }

                // Attempt reconnection
                if (autoReconnect && reconnectAttempts < maxReconnectAttempts) {
                    reconnectAttempts++;
                    var delay = reconnectDelay * reconnectAttempts;
                    console.log('Attempting reconnection in ' + delay + 'ms (attempt ' + reconnectAttempts + ')');
                    setTimeout(connect, delay);
                }
            };
        } catch (error) {
            console.error('Failed to create WebSocket connection:', error);
            emitToHandlers('error', { error: 'Failed to connect' });
        }
    }

    /**
     * Handle incoming WebSocket message
     * @param {string} data - Raw message data
     */
    function handleMessage(data) {
        try {
            var message = JSON.parse(data);
            var type = message.type;
            var payload = message.data;

            console.log('WebSocket message: type=' + type, payload);

            // Emit event to registered handlers
            if (type) {
                emitToHandlers(type, payload || {});
            }
        } catch (error) {
            console.error('Failed to parse WebSocket message:', error, data);
        }
    }

    /**
     * Emit event to registered handlers (internal)
     * @param {string} event - Event name
     * @param {*} data - Event data
     */
    function emitToHandlers(event, data) {
        if (eventHandlers[event]) {
            try {
                if (data !== undefined) {
                    eventHandlers[event](data);
                } else {
                    eventHandlers[event]();
                }
            } catch (error) {
                console.error('Error in event handler for ' + event + ':', error);
            }
        }
    }

    /**
     * Send message to server
     * @param {string} action - Action type
     * @param {Object} data - Data payload
     */
    function send(action, data) {
        var message = JSON.stringify({
            action: action,
            data: data || {}
        });

        if (isConnected && ws && ws.readyState === WebSocket.OPEN) {
            ws.send(message);
        } else {
            console.warn('WebSocket not connected, queueing message');
            messageQueue.push(message);
        }
    }

    /**
     * Join a room (subscribe to topic)
     * @param {string} room - Room name
     */
    function joinRoom(room) {
        rooms.add(room);
        if (isConnected) {
            send('join', { room: room });
            console.log('Joined room:', room);
        }
    }

    /**
     * Leave a room (unsubscribe from topic)
     * @param {string} room - Room name
     */
    function leaveRoom(room) {
        rooms.delete(room);
        if (isConnected) {
            send('leave', { room: room });
            console.log('Left room:', room);
        }
    }

    /**
     * Rejoin all rooms after reconnection
     */
    function rejoinRooms() {
        rooms.forEach(function(room) {
            send('join', { room: room });
        });
    }

    /**
     * Process queued messages after connection
     */
    function processMessageQueue() {
        while (messageQueue.length > 0 && isConnected) {
            var message = messageQueue.shift();
            ws.send(message);
        }
    }

    /**
     * Register an event handler
     * @param {string} eventName - The event name
     * @param {Function} handler - The handler function
     */
    function on(eventName, handler) {
        eventHandlers[eventName] = handler;
    }

    /**
     * Remove an event handler
     * @param {string} eventName - The event name
     */
    function off(eventName) {
        delete eventHandlers[eventName];
    }

    /**
     * Emit an event to the server (Socket.IO compatibility)
     * @param {string} eventName - The event name
     * @param {*} data - The data to send
     */
    function emit(eventName, data) {
        if (!isConnected) {
            console.warn('WebSocket not connected');
            return;
        }
        send(eventName, data);
    }

    /**
     * Get connection status
     * @returns {boolean} True if connected
     */
    function getConnectionStatus() {
        return isConnected;
    }

    /**
     * Get the WebSocket instance (for compatibility)
     * @returns {Object} The WebSocket instance wrapped with event methods
     */
    function getSocket() {
        // Return a wrapper object that provides Socket.IO-like interface
        return {
            on: on,
            off: off,
            emit: emit,
            join: joinRoom,
            leave: leaveRoom,
            connected: isConnected
        };
    }

    /**
     * Disconnect from server
     */
    function disconnect() {
        autoReconnect = false;
        if (ws) {
            ws.close(1000, 'Client disconnected');
            ws = null;
            isConnected = false;
        }
    }

    // Public API
    return {
        init: init,
        on: on,
        off: off,
        emit: emit,
        send: send,
        join: joinRoom,
        leave: leaveRoom,
        isConnected: getConnectionStatus,
        getSocket: getSocket,
        disconnect: disconnect
    };
})();
