const config = window.quizBroadcast || {};
const channels = new Set();
const listeners = new Map();

let socket = null;
let reconnectTimer = null;

function eventName(name) {
    return String(name || '').replace(/^\./, '');
}

function listenerKey(channel, event) {
    return `${channel}:${eventName(event)}`;
}

function socketUrl() {
    const scheme = config.scheme === 'https' ? 'wss' : 'ws';
    const host = config.host || window.location.hostname;
    const port = Number(config.port || 8080);
    const key = encodeURIComponent(config.key || 'quiz-local-key');

    return `${scheme}://${host}:${port}/app/${key}?protocol=7&client=quizapp&version=1.0&flash=false`;
}

function send(message) {
    if (!socket || socket.readyState !== WebSocket.OPEN) return;
    socket.send(JSON.stringify(message));
}

function subscribe(channel) {
    send({
        event: 'pusher:subscribe',
        data: { channel },
    });
}

function connect() {
    if (socket && [WebSocket.CONNECTING, WebSocket.OPEN].includes(socket.readyState)) return;
    if (!config.key) return;

    socket = new WebSocket(socketUrl());

    socket.addEventListener('open', () => {
        channels.forEach(subscribe);
    });

    socket.addEventListener('message', (message) => {
        let payload;

        try {
            payload = JSON.parse(message.data);
        } catch {
            return;
        }

        if (payload.event === 'pusher:ping') {
            send({ event: 'pusher:pong', data: {} });
            return;
        }

        if (!payload.channel || payload.event?.startsWith('pusher:')) return;

        let data = payload.data || {};
        if (typeof data === 'string') {
            try {
                data = JSON.parse(data);
            } catch {
                data = {};
            }
        }

        const callbacks = listeners.get(listenerKey(payload.channel, payload.event)) || [];
        callbacks.forEach((callback) => callback(data));
    });

    socket.addEventListener('close', () => {
        clearTimeout(reconnectTimer);
        reconnectTimer = setTimeout(connect, 3000);
    });
}

window.Echo = {
    channel(name) {
        channels.add(name);
        connect();
        subscribe(name);

        const api = {
            listen(event, callback) {
                const key = listenerKey(name, event);
                const callbacks = listeners.get(key) || [];
                callbacks.push(callback);
                listeners.set(key, callbacks);
                return api;
            },
        };

        return api;
    },
};
