# JSDoc: Source: app/channel/chatBuffer.js

# Source: app/channel/chatBuffer.js

    /*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/>.*/
    const config = require('../../../config.json');
    const channelModel = require('../../schemas/channel/channelSchema');

    /**
     * Class representing a stored chat buffer
     */
    class chatBuffer{
        /**
         * Instantiates a new chat buffer for a given channel
         * @param {channelManager} server - Parent Server Object
         * @param {Mongoose.Document} chanDB  - chanDB to rehydrate buffer from
         * @param {activeChannel} channel  - Parent Channel Object
         */
        constructor(server, chanDB, channel){
            //Grab parent server and chan objects
            this.server = server;
            this.channel = channel;

            //If we have no chanDB.chatBuffer
            if(chanDB == null || chanDB.chatBuffer == null){
                //Create RAM-based buffer array
                this.buffer = [];
            //Otherwise
            }else{
                //Pull buffer from DB
                this.buffer = chanDB.chatBuffer;
            }

            //Create variables to hold timers for deciding when to write RAM buffer to DB
            //Goes off 'this.inactivityDelay' seconds after the last chat was sent, assuming it isn't interrupted by new chats
            this.inactivityTimer = null;
            this.inactivityDelay = 10;
            //Goes off 'this.busyDelay' minutes after the first chat message in the current volley of messages. Get's cancelled before being called if this.inactivityTimer goes off.
            this.busyTimer = null;
            this.busyDelay = 5;
        }

        /**
         * Adds a given chat to the chat buffer in RAM and sets any appropriate timers for DB transactions
         * @param {chat} chat - Chat object to commit to buffer
         */
        push(chat){
            //push chat into RAM buffer
            this.buffer.push(chat);

            //clear existing inactivity timer
            clearTimeout(this.inactivityTimer);

            //reset inactivity timer
            this.inactivityTimer = setTimeout(this.handleInactivity.bind(this), 1000 * this.inactivityDelay);

            //If busy timer is unset
            if(this.busyTimer == null){
                this.busyTimer = setTimeout(this.handleBusyRoom.bind(this), 1000 * 60 * this.busyDelay);
            }
        }

        /**
         * Removes the oldest item from the chat buffer
         *
         * Was originally created in-case we needed to trigger timing functions
         *
         * Left here since it seems like good form anywho, since this would be a private, or at least protected member in another language
         */
        shift(){
            this.buffer.shift();
        }

        /**
         * Called after 10 seconds of chat room inactivity
         */
        handleInactivity(){
            this.saveDB(`${this.inactivityDelay} seconds of inactivity.`);
        }

        /**
         * Called after 5 minutes of solid activity
         */
        handleBusyRoom(){
            this.saveDB(`${this.busyDelay} minutes of activity.`);
        }

        /**
         * Saves RAM-Based buffer to Channel Document in DB
         * @param {String} reason - Reason for DB save, formatted as 'x minutes/seconds of in/activity', used for logging purposes
         * @param {Mongoose.Document} chanDB - Channel Doc to work with, can be left empty for method to auto-find through channel name.
         */
        async saveDB(reason, chanDB){
            //clear existing timers
            clearTimeout(this.inactivityTimer);
            clearTimeout(this.busyTimer);
            this.inactivityTimer = null;
            this.busyTimer = null;

            //if the server is in screamy boi mode
            if(config.verbose){
                //This should eventually be replaced by a per-channel logging feature that provides access to chan admins via web front-end
                console.log(`Saving chat buffer to channel ${this.channel.name} after ${reason}.`);
            }

            //If we wheren't handed a channel
            if(chanDB == null){
                //Now that everything is clean, we can take our time with the DB :P
                chanDB = await channelModel.findOne({name:this.channel.name});
            }

            //If we couldn't find the channel
            if(chanDB == null){
                //FUCK
                throw loggerUtils.exceptionSmith(`Unable to find channel document ${this.channel.name} while saving chat buffer!`, "chat");
            }

            //Set chan doc buffer to RAM buffer
            chanDB.chatBuffer = this.buffer;

            //save chan doc to DB.
            await chanDB.save();
        }
    }

    module.exports = chatBuffer;