/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024  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 channelSettingsPage{
    constructor(){
        //Get channel name off of the URL
        this.channel = window.location.pathname.slice(3).replace('/settings','');
        //Instantiate UX handling objects, making sure to pass the channel name.
        this.rankList = new rankList(this.channel);
        this.banList = new banList(this.channel);
        this.permList = new permList(this.channel);
        this.prefrenceList = new prefrenceList(this.channel);
        this.tokeCommandList = new tokeCommandList(this.channel);
        this.deleteBtn = new deleteBtn(this.channel);
    }
}

class rankList{
    constructor(channel){
        this.channel = channel
        this.table = document.querySelector(".admin-list-table");
        this.userPrompt = document.querySelector("#new-rank-input");
        this.rankSelect = document.querySelector("#new-rank-select");
    
        //Load the userlist and setup input
        this.loadList();
        this.setupInput();
    }

    setupInput(){
        this.userPrompt.addEventListener("keydown", this.submitNewRank.bind(this));
    }

    async loadList(){
        const list = await utils.ajax.getChannelRank(this.channel);

        this.updateList(list);
    }

    async submitNewRank(event){
        if(event.key != "Enter" && event.key != null){
            //Bail out if we didn't hit enter
            return;
        }

        //Send new rank
        this.submitUserRank(this.userPrompt.value, this.rankSelect.value);

        //Clear out prompt
        this.userPrompt.value = "";
    }

    async submitUpdate(event){
        const user = event.target.id.replace("channel-rank-select-","");
        const rank = event.target.value;

        await this.submitUserRank(user, rank);
    }

    async submitUserRank(user, rank){
        await this.updateList(await utils.ajax.setChannelRank(this.channel, user, rank));
    }

    async updateList(data){
        //If no data
        if(!data){
            //Do not pass go, do not collect $200
            return;
        }
        //Get name/rank of logged in user
        const curName = document.querySelector("#username").textContent
        const curUser = data[curName];
        const rankEnum = await utils.ajax.getRankEnum();

        //clear the table
        this.clearTable();

        //For each user in the list
        Object.entries(data).forEach((userAr) => {
            //pull user object from entry array
            const user = userAr[1];
            
            //Create IMG node inside of IMG cell
            const imgNode = document.createElement('img');
            imgNode.classList.add("admin-list-entry","admin-list-entry-item");
            imgNode.src = user.img;

            //If the listed user rank is equal or higher than the signed-in user
            if(rankEnum.indexOf(user.rank) >= rankEnum.indexOf(curUser.rank)){
                var rankContent = user.rank;
            }else{
                //Create rank select
                var rankContent = document.createElement('select');
                rankContent.id = `channel-rank-select-${user.user}`
                rankContent.classList.add("channel-rank-select")
                rankContent.addEventListener("change", this.submitUpdate.bind(this));

                //for each rank in the enum
                rankEnum.slice().reverse().forEach((rank) => {
                    //Create an option for the given rank
                    const rankOption = document.createElement('option');
                    rankOption.value = rank;
                    rankOption.innerHTML = rank;
                    rankOption.selected = user.rank == rank;
                    rankContent.appendChild(rankOption);
                });
            }

            //Generate row and append to table
            this.table.appendChild(utils.ux.newTableRow([
                imgNode,
                user.id,
                user.user,
                rankContent
            ]));
        });
    }

    clearTable(){
        //get all non-title table rows
        const rows = this.table.querySelectorAll("tr.gen-row");

        //for each row
        rows.forEach((row) => {
            //The Lord Yeeteth, and The Lord Yoinketh away...
            row.remove();
        });
    }
}

class banList{
    constructor(channel){
        this.channel = channel;
        this.table = document.querySelector("#admin-ban-list-table");
        this.banPrompt = document.querySelector("#new-ban-input");
        this.banButton = document.querySelector("#new-ban-button");

        this.loadList();
        this.setupInput();
    }

    setupInput(){
        this.banButton.addEventListener('click', this.ban.bind(this));
    }

    async loadList(){
        const data = await utils.ajax.getChanBans(this.channel);
        this.updateList(data);
    }

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

    async ban(event){
        new banUserPopup(this.channel, this.banPrompt.value, this.updateList.bind(this));
    }

    async unban(event){
        //Rip user outta the target id
        const user = event.target.id.replace("admin-user-list-unban-icon-","");
        //Tell the server to unban them and get the list returned
        const list = await utils.ajax.chanUnban(this.channel, user);

        //Use the list to update the UI
        this.updateList(list);
    }

    updateList(bans){
        //for each ban listed
        this.clearList();
        bans.forEach((ban)=>{
            //Create IMG node
            const imgNode = document.createElement('img');
            imgNode.classList.add("admin-list-entry","admin-list-entry-item");
            imgNode.src = ban.user.img;

            //Create banAlts check
            const banAlts = document.createElement('i');
            banAlts.classList.add("admin-user-list-ban-alts",ban.banAlts ? "bi-check" : "bi-x");

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

            //THIS IS NOT YET PROPERLY IMPLEMENTED AS getDaysUntilExpiration has not been made for chan bans
            const expirationString = ban.expirationDays < 0 ? "Never" :  `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;

            //Generate row and append to table
            this.table.appendChild(utils.ux.newTableRow([
                imgNode,
                ban.user.id,
                ban.user.user,
                banAlts,
                new Date(ban.banDate).toDateString(),
                expirationString,
                unbanIcon
            ]));
        });
    }
}

class banUserPopup{
    constructor(channel, target, cb){
        this.channel = channel;
        this.target = target;
        this.cb = cb;
        this.popup = new canopyUXUtils.popup("channelUserBan", true, this.asyncConstruction.bind(this));
    }

    asyncConstruction(){
        this.title = document.querySelector(".popup-title");
        //Setup title text real quick-like :P
        this.title.innerHTML = `Ban ${this.target}`;

        this.permBan = document.querySelector("#ban-popup-perm");
        this.expiration = document.querySelector("#ban-popup-expiration");
        this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix");
        this.banAlts = document.querySelector("#ban-popup-alts");
        this.banButton = document.querySelector("#ban-popup-ban-button");
        this.cancelButton = document.querySelector("#ban-popup-cancel-button");

        this.setupInput();
    }

    setupInput(){
        this.permBan.addEventListener("change", this.permaBanLabel.bind(this));
        this.cancelButton.addEventListener("click", this.popup.closePopup.bind(this.popup));
        this.banButton.addEventListener("click",this.ban.bind(this));
    }

    permaBanLabel(event){
        if(event.target.checked){
            this.expiration.disabled = true;
        }else{
            this.expiration.disabled = false;
        }
    }

    async ban(event){
        //Get expiration days
        const expirationDays = this.permBan.checked ? -1 : this.expiration.value;
        //Send ban request off to server and retrieve new ban list
        const data = await utils.ajax.chanBan(this.channel, this.target, expirationDays, this.banAlts.checked);

        //Close the popup
        this.popup.closePopup();

        //If we have data and a callback, run the callback with our data 
        if(data != null && this.cb != null){
            await this.cb(data);
        }
    }
}

class prefrenceList{
    constructor(channel){
        this.channel = channel;
        this.inputs = document.querySelectorAll(".channel-preference-list-item");

        this.setupInput();
    }

    setupInput(){
        this.inputs.forEach((input) => {
            input.addEventListener("change", this.submitUpdate.bind(this));
        });
    }

    async submitUpdate(event){
        const key = event.target.id.replace("channel-preference-","");
        const value = event.target.checked;
        const settingsMap = new Map([
            [key, value]
        ]);

        this.handleUpdate(await utils.ajax.setChannelSetting(this.channel, settingsMap), event.target, key);
    }

    handleUpdate(data, target, key){
        if(data){
            target = data[key];
        }
    }
}

class permList{
    constructor(channel){
        this.channel = channel
        this.inputs = document.querySelectorAll(".channel-perm-select");
        this.setupInput();
    }

    setupInput(){
        this.inputs.forEach((input) => {
            input.addEventListener("change", this.submitUpdate.bind(this));
        });
    }

    async submitUpdate(event){
        const key = event.target.id.replace("admin-perm-list-rank-select-","");
        const value = event.target.value;
        const permMap = new Map([
            [key, value]
        ]);

        this.handleUpdate(await utils.ajax.setChannelPermissions(this.channel, permMap), event.target, key);
    }

    handleUpdate(data, target, key){
        target.value = data[key];
    }
}

class tokeCommandList{
    constructor(channel){
        this.channel = channel;
        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 utils.ajax.addChanToke(this.channel, this.newTokeCommandPrompt.value);
        //clear the prompt
        this.newTokeCommandPrompt.value = "";
        //render the returned list
        this.renderTokeList(tokeList);
    }

    async getTokeList(){
        const tokeList = await utils.ajax.getChanTokes(this.channel);
        this.renderTokeList(tokeList);
    }

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

    async deleteToke(event){
        const name = event.target.id.replace("toke-command-delete-","");

        const tokeList = await utils.ajax.deleteChanToke(this.channel, 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.innerHTML = `!${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.id = `toke-command-delete-${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 deleteBtn{
    constructor(channel){
        this.channel = channel;
        this.delete = document.querySelector("#chan-delete");
    
        this.setupInput();
    }

    setupInput(){
        this.delete.addEventListener('click', () => {new deleteAccountPopup(this.channel)});
    }

}

class deleteAccountPopup{
    constructor(channel){
        this.channel = channel;
        this.popup = new canopyUXUtils.popup("nukeChannel", true, this.asyncConstructor.bind(this));
    }

    asyncConstructor(){
        this.prompt = document.querySelector("#delete-channel-popup-prompt");
        this.label = document.querySelector("#delete-channel-popup-content");

        //Fill channel label
        this.label.innerHTML = this.label.innerHTML.replaceAll("[CHANNEL]", this.channel);

        this.setupInput();
    }

    setupInput(){
        this.prompt.addEventListener("keydown", this.nukeAccount.bind(this));
    }

    async nukeAccount(event){
        if(event.key == "Enter"){
            console.log(this.channel);
            if(this.channel === event.target.value){
                await utils.ajax.deleteChannel(this.channel, event.target.value);
            }
        }
    }
}

const channelSettings = new channelSettingsPage();