/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.*/

//local includes
const auxServer = require('../auxServer');
const chatPreprocessor = require('../chatPreprocessor');
const loggerUtils = require("../../utils/loggerUtils");
const message = require("./message");
const socketUtils = require("../../utils/socketUtils");

/**
 * Class containg global server-side private message relay logic
 */
class pmHandler extends auxServer{
    /**
     * Instantiates object containing global server-side private message relay logic
     * @param {Socket.io} io - Socket.io server instanced passed down from server.js 
     * @param {channelManager} chanServer - Sister channel management server object
     */
    constructor(io, chanServer){
        super(io, chanServer, "/pm");

        this.chatPreprocessor = new chatPreprocessor(null, null);
    }

    /**
     * Handles global server-side initialization for new connections to the private messaging system
     * @param {Socket} socket - Requesting Socket
     */
    async handleConnection(socket){
        //Check if we're properly authorized
        const authorized = await super.handleConnection(socket);

        //If we're authorized
        if(authorized != null){
            //Throw the user into their own unique channel
            socket.join(socket.user.user);

            //Define listeners
            this.defineListeners(socket);
        }
    }

    defineListeners(socket){
        super.defineListeners(socket);
        socket.on("pm", (data)=>{this.handlePM(data, socket)});
    }

    async handlePM(data, socket){
        try{
            //Check recipients
            const recipients = this.checkRecipients(data.recipients, socket);

            //If we don't have any valid recipients
            if(recipients.length <= 0){
                //Drop that shit
                return false;
            }

            //If this is a sesh starter
            if(data.msg == '' || data.msg == null){
                //Skip pre-processing and send a cooked message
                return this.relayPMObj(new message(
                    socket.user.user,
                    recipients,
                    '',
                    'chat',
                    []
                ));
            }

            //preprocess message
            const preprocessedMessage = await this.chatPreprocessor.preprocess(socket, data);

            //If the send flag wasnt thrown false
            if(preprocessedMessage != false){
                //Pull an active user profile from the first channel that gives it in the chan server
                const senderProfile =  this.chanServer.activeUsers.get(socket.user.user);

                //If user isn't actively connected to a channel
                if(senderProfile == null || senderProfile.length == 0){
                    //They don't get to send shit lol
                    return;
                }

                //Create message object and relay it off to the recipients
                this.relayPMObj(new message(
                    socket.user.user,
                    recipients,
                    senderProfile[0].flair,
                    senderProfile[0].highLevel,
                    preprocessedMessage.message,
                    preprocessedMessage.chatType,
                    preprocessedMessage.links
                ));
            }

        //If something fucked up
        }catch(err){
            //Bitch and moan
            return loggerUtils.socketExceptionHandler(socket, err);
        }
    }

    relayPMObj(msg){
        //For each recipient
        for(let user of msg.recipients){
            //Send the message
            this.namespace.to(user).emit("message", msg);
        }

        //Acknowledge the sent message
        this.namespace.to(msg.user).emit("sent", msg);
    }

    /**
     * Basic function for checking presence
     * This could be done using Channel Presence, but running off of bare Socket.io functionality makes this easier to implement outside the channel if need be
     * @param {String} user - Username to check presence of
     * @returns {Boolean} Whether or not the user is currently able to accept messages
     */
    checkPresence(user){
        //Pull room map from the guts of socket.io and run a null check against the given username
        return this.namespace.adapter.rooms.get(user) != null;
    }

    checkRecipients(input, socket){
        //Create empty recipients array
        let recipients = [];

        //For each requested recipient
        for(let user of input){
            //If the given user is online and didn't send the message
            if(this.checkPresence(user) && user != socket.user.user){
                //Add the recipient to the list
                recipients.push(user);
            }
        }

        //return recipients
        return recipients;
    }

}

module.exports = pmHandler;