/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024-2026 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/>.*/

class adminUserList{
    constructor(){
        this.userNames = document.querySelectorAll(".admin-user-list-name");
        this.rankSelectors = document.querySelectorAll(".admin-user-list-rank-select");
        this.banIcons = document.querySelectorAll(".admin-user-list-ban-icon");
        this.passResetIcons = document.querySelectorAll(".admin-user-list-pw-reset-icon");

        this.setupInput();
    }

    setupInput(){
        for(let userName of this.userNames){
            //Get username from parent dataset
            const name = userName.closest('tr').dataset.name; 

            userName.addEventListener('mouseenter',(event)=>{utils.ux.displayTooltip(event, `altList?user=${name}`, true);});
        }

        for(let rankSelector of this.rankSelectors){
            rankSelector.addEventListener("change", this.setRank.bind(this))
        }

        for(let banIcon of this.banIcons){
            banIcon.addEventListener("click", this.banPopup.bind(this));
        }

        for(let passResetIcon of this.passResetIcons){
            passResetIcon.addEventListener("click", this.genResetLink.bind(this))
        }
    }

    async setRank(event){
            const user = event.target.closest('tr').dataset.name; 
            const rank = event.target.value;

            this.updateSelect(await adminUtil.setUserRank(user, rank), event.target);
    }

    async genResetLink(event){
        //Scrape user
        const user = event.target.closest('tr').dataset.name; 

        const URL = (await adminUtil.genPasswordResetLink(user)).url;

        //Create span
        const span = document.createElement('span');
        //Usually not into doing CSS this way, but I'm not making a dedicated file for a popup this small...
        span.style = "text-align: center; display: block;"

        //Create header
        const header = document.createElement('h3');
        header.innerText = `Reset Link for ${user}`

        //Create link
        const link = document.createElement('a');
        link.innerText = "Reset Link"
        link.href = URL;

        //Append link to the header
        span.appendChild(header);
        span.appendChild(link);

        //Display link in pop-up
        new canopyUXUtils.popup(span.outerHTML);

    }

    banPopup(event){
        const user = event.target.closest('tr').dataset.name; 
        new banUserPopup(user, userBanList.renderBanList.bind(userBanList));
    }

    updateSelect(update, select){
        if(update != null){
            select.value = update.rank;
        }
    }

}

class adminPermissionList{
    constructor(){
        this.permissionSelectors = document.querySelectorAll(".admin-perm-list-rank-select");
        this.channelPermissionSelectors = document.querySelectorAll(".admin-chan-perm-list-rank-select");

        this.setupInput();
    }

    setupInput(){
        this.permissionSelectors.forEach((permissionSelector)=>{
            permissionSelector.addEventListener("change", this.setPerm.bind(this))
        });

        this.channelPermissionSelectors.forEach((permissionSelector)=>{
            permissionSelector.addEventListener("change", this.setChanPerm.bind(this))
        });
    }

    async setPerm(event){
        const permMap = new Map([[event.target.dataset.key, event.target.value]]);

        this.updateSelect(await adminUtil.setPermission(permMap), event.target);
    }

    async setChanPerm(event){
        const permMap = new Map([[event.target.dataset.key, event.target.value]]);

        this.updateChanSelect(await adminUtil.setChannelOverride(permMap), event.target);
    }

    updateSelect(update, select){
        if(update != null){
            var perm = select.dataset.key;

            select.value = update[perm];
        }
    }

    updateChanSelect(update, select){
        if(update != null){
            var perm = select.dataset.key;

            select.value = update.channelOverrides[perm];
        }
    }
}

class adminUserBanList{
    constructor(){
        this.table = document.querySelector("#admin-ban-list-table");
        
        this.getBanList();
    }

    async getBanList(){
        this.renderBanList(await adminUtil.getBans());
    }

    clearBanList(){
        const oldRows = this.table.querySelectorAll('tr.gen-row');
        oldRows.forEach((row) => {
            row.remove();
        });
    }

    async unban(event){
        //Get username from target id
        const user = event.target.dataset.name;
        //Send unban command to server and display the resulting banlist
        this.renderBanList(await adminUtil.unbanUser(user));
    }

    renderBanList(banList){
        //Clear out the ban list
        this.clearBanList();

        //For each ban received
        banList.forEach((ban) => {
            //Calculate expiration date and expiration days
            let expirationDateString = `${new Date(ban.expirationDate).toLocaleDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
            let banActionString = ban.permanent ? "Nuke<br>Accounts" : "Un-Ban";
            let nuked = ban.user == null;

            //If the user is null (the ban has been nuked)
            if(nuked){
                //Fudge the user object if it's already been deleted
                ban.user = {
                    //Use dead name if we got one
                    user: ban.deletedNames[0] != null ? ban.deletedNames[0] : "Nuked"
                };

                ban.alts[0] = {
                    user: "Nuked"
                }

                //Fake the display string
                expirationDateString = "Accounts<br>Nuked"
                banActionString = "Accounts<br>Nuked"
            }

            //Generate and append row to table
            this.table.appendChild(utils.ux.newTableRow([
                this.renderUser(ban.user, nuked),
                this.renderUsers(ban.alts, nuked),
                this.renderDeadUsers(ban.deletedNames),
                this.renderIPs(ban.ips),
                new Date(ban.banDate).toLocaleDateString(),
                expirationDateString,
                banActionString,
                this.renderIcons(ban.user)
            ]));
        });
    }

    renderUser(user, nuked = false){
        if(nuked){
            var userNode = document.createElement('img');
            userNode.classList.add("admin-list-entry","admin-list-entry-item");
            userNode.src = '/img/nuked.png'
            userNode.title = "Nuked"
        }else{
            var userNode = document.createElement('p');
            userNode.textContent = utils.unescapeEntities(user.user);
        }
        return userNode;
    }

    renderUsers(users, nuked){
        //Create userlist span
        let userList = document.createElement('span');

        if(!nuked){
            //For each user
            for(let user of users){
                //Render out the user
                userList.appendChild(this.renderUser(user));
            }
        }else{
            userList = document.createElement('img');
            userList.classList.add("admin-list-entry","admin-list-entry-item");
            userList.src = '/img/nuked.png'
            userList.title = "Nuked"
        }

        //return our list
        return userList;
    }

    renderDeadUsers(users){
        //Create userlist span
        const deadUsers = document.createElement('span');

        //For each ip
        for(let user of users){
            //Create a node
            const userNode = document.createElement('p');
            //Fill it wit the ip
            userNode.textContent = utils.unescapeEntities(user);
            //Append it
            deadUsers.appendChild(userNode);
        }

        //return our list
        return deadUsers;
    }   

    renderIPs(ips){
        //Create userlist span
        const ipList = document.createElement('span');

        //For each ip
        for(let ip of ips.plaintext){
            //Create a node
            const ipNode = document.createElement('p');
            //Fill it wit the ip
            ipNode.textContent = utils.unescapeEntities(ip);
            //Append it
            ipList.appendChild(ipNode);
        }

        //For each user
        for(let hash of ips.hashed){
            //Create a node
            const ipNode = document.createElement('p');
            //List it as a hashed ip with the hash as alt text
            ipNode.textContent = utils.unescapeEntities('[Hashed IP]');
            ipNode.title = hash;
            //Append the node
            ipList.appendChild(ipNode);
        }

        //return our list
        return ipList;
    }

    renderIcons(user){
        //Create unban icon
        const unbanIcon = document.createElement('i');
        unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon");
        unbanIcon.dataset.name = user.user;
        unbanIcon.title = `Unban ${user.user}`;
        unbanIcon.addEventListener("click", this.unban.bind(this));

        //Create nuke account icon
        const nukeAccount = document.createElement('i');
        nukeAccount.classList.add("bi-radioactive","admin-user-list-icon","admin-user-list-unban-icon");
        nukeAccount.dataset.name = user.user;
        nukeAccount.title = `Nuke accounts`;
        nukeAccount.addEventListener("click",console.log);

        //If our user has been deleted don't return the nuke icon
        return (user.deleted ? unbanIcon : [unbanIcon, nukeAccount]);
    }
}

class adminTokeCommandList{
    constructor(){
        this.tokeCommandList = document.querySelector('div.toke-command-list');
        this.newTokeCommandPrompt = document.querySelector('#new-toke-command-input');
        this.newTokeCommandButton = document.querySelector('#new-toke-command-button');

        //Setup input
        this.setupInput();

        //Pull the toke list on load
        this.getTokeList();
    }

    setupInput(){
        this.newTokeCommandButton.addEventListener('click', this.addToke.bind(this));
    }

    async addToke(event){
        //Send out the new toke command and get the new list
        const tokeList = await adminUtil.addTokeCommand(this.newTokeCommandPrompt.value);
        //clear the prompt
        this.newTokeCommandPrompt.value = "";
        //render the returned list
        this.renderTokeList(tokeList);
    }

    async getTokeList(){
        const tokeList = await adminUtil.getTokeCommands();
        this.renderTokeList(tokeList);
    }

    clearTokeList(){
        this.tokeCommandList.innerHTML = "";
    }

    async deleteToke(event){
        const name = event.target.dataset.toke;

        const tokeList = await adminUtil.deleteTokeCommand(name);

        this.renderTokeList(tokeList);
    }

    renderTokeList(tokeList){
        if(tokeList != null){
            //Clear our the toke list
            this.clearTokeList();

            //For each toke in the received list
            tokeList.forEach((toke)=>{
                //generate a toke command span, and append it to the toke list div
                this.tokeCommandList.appendChild(this.generateTokeSpan(toke));
            });
        }
    }

    generateTokeSpan(toke){
        //Create toke command span
        const tokeSpan = document.createElement('span');
        tokeSpan.classList.add('toke-command-list');

        //Create toke command label
        const tokeLabel = document.createElement('p');
        tokeLabel.textContent = utils.unescapeEntities(`!${toke}`);
        tokeLabel.classList.add('toke-command-list');

        //Create toke command delete icon
        const tokeDelete = document.createElement('i');
        tokeDelete.classList.add('toke-command-list', 'bi-trash-fill', 'toke-command-delete');
        tokeDelete.dataset.toke = toke;
        tokeDelete.addEventListener('click', this.deleteToke.bind(this));

        //append span contents to tokeSpan
        tokeSpan.appendChild(tokeLabel);
        tokeSpan.appendChild(tokeDelete);

        //return the toke span
        return tokeSpan
    }
}

class adminEmoteList{
    constructor(){
        this.linkPrompt = document.querySelector('#new-emote-link-input');
        this.namePrompt = document.querySelector('#new-emote-name-input');
        this.addButton = document.querySelector('#new-emote-button');
        this.emoteList = document.querySelector('#emote-list');

        //Setup input
        this.setupInput();

        //Pull and render emote list
        this.updateList();
    }

    setupInput(){
       this.addButton.addEventListener('click', this.addEmote.bind(this));
    }

    async deleteEmote(event){
        //Strip name from element id
        const name = event.target.dataset.name;

        //Delete emote and pull list
        const list = await adminUtil.deleteEmote(name);

        //If we received a list
        if(list != null){
            //Pass updated liste to renderEmoteList function instead of pulling it twice
            this.renderEmoteList(list);
        }
    }

    async addEmote(event){
        //Add emote to list and ingest returned updates list
        const list = await adminUtil.addEmote(this.namePrompt.value, this.linkPrompt.value);

        //If we received a list
        if(list != null){
            //Pass updated liste to renderEmoteList function instead of pulling it twice
            this.renderEmoteList(list);

            //Clear out the prompts
            this.namePrompt.value = '';
            this.linkPrompt.value = '';
        }
    }

    async updateList(){
        const list = await adminUtil.getEmotes();
        this.renderEmoteList(list);
    }

    renderEmoteList(list){
        //Clear the current list
        this.emoteList.innerHTML = "";

        //For each emote in the list
        list.forEach((emote) => {
            //Create span to hold emote
            const emoteDiv = document.createElement('div');
            emoteDiv.classList.add('emote-list-emote');

            //If the emote is an image
            if(emote.type == 'image'){
                //Create image node
                var emoteMedia = document.createElement('img');
            //if emote is a video
            }else if(emote.type == 'video'){
                //create video node
                var emoteMedia = document.createElement('video');
                //Set video properties
                emoteMedia.autoplay = true;
                emoteMedia.muted = true;
                emoteMedia.controls = false;
                emoteMedia.loop = true;
            }

            //set media link as source
            emoteMedia.src = emote.link;
            //Set media class
            emoteMedia.classList.add('emote-list-media');

            //Create title span
            const titleSpan = document.createElement('span');
            titleSpan.classList.add('emote-list-title');

            //Create paragraph tag
            const emoteTitle = document.createElement('p');
            //Set title class
            emoteTitle.classList.add('emote-list-title');
            //Set emote title
            emoteTitle.textContent = utils.unescapeEntities(`[${emote.name}]`);
            
            //Create delete icon
            const deleteIcon = document.createElement('i');
            //Set delete icon id and class
            deleteIcon.classList.add('bi-trash-fill', 'emote-list-delete');
            deleteIcon.dataset.name = emote.name;
            //Add delete icon event listener
            deleteIcon.addEventListener('click',this.deleteEmote.bind(this));


            //Add the emote media to the emote span
            emoteDiv.appendChild(emoteMedia);
            //Add title paragraph node
            titleSpan.appendChild(emoteTitle);
            //Add trash icon node
            titleSpan.appendChild(deleteIcon);
            //Add title span
            emoteDiv.appendChild(titleSpan);

            //Append the mote span to the emote list
            this.emoteList.appendChild(emoteDiv);
        });
    }
}

const userList = new adminUserList();
const permissionList = new adminPermissionList();
const userBanList = new adminUserBanList();
const tokeCommandList = new adminTokeCommandList();
const emoteList = new adminEmoteList();