const WebSocket = require('ws');
const { serverConfig, configExists } = require('./server_config');
const { logChat } = require('./console');
const helpers = require('./helpers');
const storage = require('./storage.js');

function heartbeat() { // WebSocket heartbeat helper
    this.isAlive = true;
}

function createChatServer() {
    if (!serverConfig.webserver.chatEnabled) return;

    const chatWss = new WebSocket.Server({ noServer: true });

    chatWss.on('connection', (ws, request) => {
        ws.isAlive = true;
        ws.on('pong', heartbeat);

        const clientIp = helpers.getIpAddress(request);
        const userCommandHistory = {};

        // Send chat history safely
        storage.chatHistory.forEach((message) => {
            const historyMessage = { ...message, history: true };

            if (!request.session?.isAdminAuthenticated) delete historyMessage.ip;
            ws.send(JSON.stringify(historyMessage));
        });

        ws.send(JSON.stringify({
            type: 'clientIp',
            ip: clientIp,
            admin: request.session?.isAdminAuthenticated
        }));


        const userCommands = {};
        let lastWarn = { time: 0 };

        ws.on('message', (message) => {
            message = helpers.antispamProtection(
                message,
                clientIp,
                ws,
                userCommands,
                lastWarn,
                userCommandHistory,
                '5',
                'chat',
                512
            );

            if(!message) return;

            let messageData;

            try {
                messageData = JSON.parse(message);
            } catch {
                ws.send(JSON.stringify({ error: "Invalid message format" }));
                return;
            }

            delete messageData.admin;
            delete messageData.ip;
            delete messageData.time;

            if (messageData.nickname != null) messageData.nickname = helpers.escapeHtml(String(messageData.nickname));

            messageData.ip = clientIp;

            const now = new Date();
            messageData.time = String(now.getHours()).padStart(2, '0') + ":" + String(now.getMinutes()).padStart(2, '0');

            if (serverConfig.webserver.banlist?.includes(clientIp)) {
                ws.close(1008, 'Banned IP');
                return;
            }

            if (request.session?.isAdminAuthenticated === true) messageData.admin = true;
            if (messageData.nickname?.length > 32) messageData.nickname = messageData.nickname.substring(0, 32);
            if (messageData.message?.length > 255) messageData.message = messageData.message.substring(0, 255);

            storage.chatHistory.push(messageData);
            if (storage.chatHistory.length > 50) storage.chatHistory.shift();

            logChat(messageData);

            chatWss.clients.forEach((client) => {
                if (client.readyState === WebSocket.OPEN) {
                    const responseMessage = { ...messageData };
                    if (!request.session?.isAdminAuthenticated) delete responseMessage.ip;

                    client.send(JSON.stringify(responseMessage));
                }
            });
        });

        ws.on('close', () => {
            ws.isAlive = false;
        });
    });

    /**
    * We will not always be receiving data, so some proxies may terminate the connection, this prevents it.
    */
    const interval = setInterval(() => {
        chatWss.clients.forEach((ws) => {
            if (ws.isAlive === false) return ws.terminate();
            ws.isAlive = false;
            ws.ping();
        });
    }, 30000);

    chatWss.on('close', () => {
        clearInterval(interval);
    });

    storage.websocket_delegation.set("/chat", chatWss);
}

if(!configExists()) return;
createChatServer();