const GuildM = require('../models/guilds');
const roleRulesM = require('../models/autoRoleRule');
const feedsM = require('../models/feeds');
const leagueGameM = require('../models/leagueGame');
const leagueM = require('../models/league');
const xmlparser = require('xml-js')
const {isValidObjectId} = require('mongoose')

exports.getUser = (io)=>
{
    return(req,res)=>
    {
        fetch('https://discord.com/api/users/@me', 
        {
            headers: {
                authorization: req.headers.authorization,
            },
        })
        .then(result=>result.json())
        .then(response => 
        {
            res.json(response);
        })
    }
};

exports.guildData = (io,bot)=>
{
    return async (req, res)=>
    {
        let userGuilds =  await fetch('https://discord.com/api/users/@me/guilds',
                                {
                                    headers: {
                                        authorization: req.headers.authorization,
                                    },
                                })
                                .then(result => result.json())
                                .then(response => 
                                {
                                    
                                    if(response.message) return response.message
                                    return response.filter(x=>x.owner==true);
                                })
        var promises = [];
        if(typeof userGuilds == 'string') return res.json({Error:userGuilds})
        for(var guild of userGuilds) {
            promises.push(new Promise((resolve, reject) => {
                (async (guild)=>
                {
                    
                    var aux = await getSpecificGuildData(guild.id);
                    var roleRules = aux ? await getRoleRules(guild.id):null;
                    const dataObject =  
                    {
                        id:guild.id,
                        music:aux.guild?aux.guild.guild[0].music:false,
                        strikes:aux.guild?aux.guild.guild[0].strikes:false,
                        name:guild.name,
                        icon:guild.icon?`https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.webp`:'https://cdn3.iconfinder.com/data/icons/popular-services-brands-vol-2/512/discord-512.png',
                        features:guild.features,
                        hasRem:aux.extra?true:false,
                        memberCount:aux.extra?aux.extra.memberCount:null,
                        roleRules
                    };
                    resolve(dataObject);

                })(guild)
            }));
        }
        Promise.all(promises).then((values) => {
            return res.json(values);
        });
   
    }
    async function getSpecificGuildData(guildID)
    {
        var extra = bot.guilds.cache.get(guildID)
        var guild = await GuildM.find({gID:guildID})
            .then((guild,err)=>
            {
                if(err || guild.length==0) return;
                return {guild};
            }) 
        return {guild, extra};
    }
    async function getRoleRules(guildid)
    {
        return await roleRulesM.find({gID:guildid}).then((err, rules)=>
        {
            if(err || rules.length==0) return 
            return rules
        })
    }
}


exports.updateGuild = (io)=>
{
    return (req, res)=>
    {
        
        GuildM.findOne({gID:req.body.guildID})
        .then(
            (guild, err)=>
            {
                if(err || guild.length) return res.json({Error:'No guild'});
                
                guild[`${req.body.property}`] =! guild[`${req.body.property}`];
                GuildM.updateOne({gID:req.body.guildID}, guild).then(()=>
                {
                    return res.json(guild);    
                })
            })
    }
}

exports.getMessage = (bot)=>
{
    return async (req, res)=>
    {
        if(!req.headers.guildid && !req.headers.messageid) return res.json({error:'No gid or messageid or both.'})
        const TextChannels = bot.guilds.cache.get(req.headers.guildid).channels.cache.filter(x=>x.type==0);
        TextChannels.forEach(channel => {
            (async (channel)=>
            {
                var message
                try {
                    message = await channel.messages.fetch(req.headers.messageid);
                } catch (error) {
                    //message doesn't exist on this channel or at all
                }
                if(message)
                {
                    message = message.content
                    if(message.split('***').length %2 == 1) message = message.replaceAll('***', '');
                    if(message.split('**').length %2 == 1) message = message.replaceAll('**', '');
                    if(message.split('*').length %2 == 1) message = message.replaceAll('*', '');
                    if(message.split('~~').length %2 == 1) message =  message.replaceAll('~~', '');
                    if(message.split('```').length %2 == 1) message =  message.replaceAll('```', '');
                    if(message.split('``').length %2 == 1) message =  message.replaceAll('``', '');
                    if(message.split('`').length %2 == 1) message =  message.replaceAll('`', '');
                    res.json({message})
                }
            })(channel)
        });
    }
}
exports.getRules = async (req,res)=>
{
    var payload;
    if(req.headers.guildid)
    {
        payload =  await roleRulesM.find({gID:req.headers.guildid})
                .then(rules =>{return rules})
                .catch(()=>{return null})
        return res.json(payload);
    }
    payload = await roleRulesM.findOne({_id:req.headers.id})
                            .then(rule=>{return rule})
                            .catch(()=>{return null})
    return res.json(payload);
}
exports.deleteRule = async (req, res)=>
{
    var deleted = await roleRulesM.findByIdAndDelete(req.headers.ruleid)
        .then(()=>{return {success:'Done'}})
        .catch(()=>{return {error:'Failed to delete'}});
    res.json(deleted);
}
exports.updateRule = (bot)=>
{
    return async (req, res)=>
    {
        let args = req.headers;
        if(args.isnew == 'true')
        {
            const validationResult = await confirmArgs(args);
            if (validationResult.error) return res.json({error:validationResult.error})
            var rule = new roleRulesM();
            rule.gID = validationResult.gID;
            rule.mID = validationResult.mID;
            rule.roleID = validationResult.roleID;
            rule.roleEmoji = validationResult.roleEmoji;
            rule.roleName = validationResult.roleName;         
            rule.save(err=>
                {
                    if(err)res.json(err);
                    res.json({success:true,rule})
                })
        }
        else
        {
            const validationResult = await confirmArgs(args);
            if (validationResult.error) return res.json({error:validationResult.error})
            roleRulesM.findOne({id:args.id}).then(rule=>
                {
                    if(!rule) return res.json({error:'DB Error'});
                    rule.gID = validationResult.gID;
                    rule.mID = validationResult.mID;
                    rule.roleID = validationResult.roleID;
                    rule.roleEmoji = validationResult.roleEmoji;
                    rule.roleName = validationResult.roleName;      
                    roleRulesM.updateOne({_id:rule.id}, rule,{upsert:true}).then(updatedG=>
                    {
                        res.json({success:true, rule})
                    })

                })
        }
    }
    async function confirmArgs(args)
    {
        var mID = args.messageid;
        var roleID = args.roleid;
        var roleEmoji = args.emojiid;
        var gID = args.gid;
        var roleName;
        if(!mID) return {error:'Message ID'}
        if(!roleID) return {error:'Role ID'}
        if(!roleEmoji) return {error:'Emoji ID'}
        if(!gID) return {error:'Guild ID'}
        var m = await (async ()=>
        {
            for(const [id, channel] of bot.channels.cache)
            {
                if(channel.type == 0)
                {
                    try {
                        const exists = await bot.channels.cache.get(id).messages.fetch(mID);
                        if(exists) return m = true;
                    } catch (error) {}
                }
            }
        })();
        const r = bot.guilds.cache.get(gID).roles.cache.get(roleID);
        const e =  bot.guilds.cache.get(gID).emojis.cache.get(roleEmoji);
        bot.guilds.cache.get(gID).emojis.cache.forEach(emoji=>
            {
                if(emoji.name == 'tuturu') console.log(emoji)
            })
        console.log('Emoji', roleEmoji, e)  
        if(!m || !r || !e) return m?(r?(e?false:{error:'Emoji ID'}):{error:'Role ID'}):{error:'Message ID'}
        roleName = r.name;
        return {mID, roleID, roleEmoji, roleName, gID}
    }
}
exports.getFeeds = async (req, res)=>
{
    var payload = await feedsM.find({gID:req.headers.guildid}).then(res=>{return res}).catch(err=>{return err});
    res.json(payload)
   
}
exports.getFeed = async (req, res)=>
{
    var payload = await feedsM.find({_id:req.headers.feedid}).then(res=>{return res}).catch(err=>{return err});
    res.json(payload)
   
}
exports.getChannelName = async (req,res)=>
{
    var payload = await fetch('https://www.youtube.com/feeds/videos.xml?channel_id='+req.headers.channelid,
    {
        method: "GET",
        mode: "cors", 
    })
    .then(response=>
        {
            if(response.ok) return response.text();
        })
    .then(data=>
        {
            data = xmlparser.xml2json(data,
                {
                    compact: true,
                    space: 4
                });
                data = JSON.parse(data);
            return data.feed.author.name._text
        })
    .catch(error=>
        {
            return error;
        });
    res.json(payload);
    
}

exports.addFeed = (bot)=>
{
    return async (req, res)=>
    {
        var headers=  req.headers;
        var args = [headers.gid, headers.dcchannelid, headers.ytchannelid]
        const validationResult = await confirmArgs(args, bot);
        // console.log('Before new or old check', validationResult)
        if (validationResult.error) return res.json({error:validationResult.error});
        if(!headers.feedid)
        {
            //new
            var feed = new feedsM();
            feed.gID = headers.gid;
            feed.ChannelId = validationResult.channelId
            feed.YTChannelId = validationResult.YTChannelId;
            feed.CostumMessage = headers.costummessagei!=''?headers.costummessagei:'New video:';
            feed.save(err=>
                {
                    if(err)res.json({error:err});
                    res.json({success:true, feed})
                })
        }
        else
        {
            feedsM.findOne({id:args.id}).then(feed=>
                {
                    if(!feed) return res.json({error:'DB Error'});
                    feed.gID = headers.gid;
                    feed.ChannelId = validationResult.channelId
                    feed.YTChannelId = validationResult.YTChannelId;
                    feed.CostumMessage = headers.costummessagei!=''?headers.costummessagei:'New video:';
                    feedsM.updateOne({_id:feed.id}, feed,{upsert:true}).then(updatedG=>
                    {
                        res.json({success:true, feed})
                    })

                })
        }
    }
}
async function confirmArgs(args, bot)
{
    var channelId = args[1];
    var channelURL = args[2]
    async function getChannelId(url)
    {
    if(url.split('@').length<1) return 
    return await fetch(url)
            .then(handleResponse)
            .then(handleData)
            .catch(handleError);
            function handleResponse(response) 
            {
                if(response.ok) return response.text();
            }
            function handleError(error) 
            {
                return {error:'Bad youtube channel link.'};
            }
            function handleData(data) 
            {         
                return data ? data.split('https://www.youtube.com/feeds/videos.xml?channel_id=')[1].split('"')[0] : false;
            }
    }

    const YTChannelId = await getChannelId(channelURL);
    if(YTChannelId.error) return {error:YTChannelId.error}
    const discordChannel = await bot.channels.fetch(channelId).then(channel=>{return channel }).catch(()=>{return null});
    if(!discordChannel) return {error:'Provided discord channel id does not exist.'}   
    var name = await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelId}`)
                        .then(response =>
                            {
                                return response.text()
                            })
                        .then(data=>
                            {

                                data = xmlparser.xml2json(data,
                                                {
                                                    compact: true,
                                                    space: 4
                                                });
                                data = JSON.parse(data);
                                return data.feed.author.name._text
                            })
                        .catch(error=>{return error;});
    return {YTChannelId, channelId, name, channelName:discordChannel.name}
}
exports.deleteFeed = (req,res) =>
{
    feedsM.findOneAndDelete({_id:req.headers.feedid}).then(()=>{res.json({success:true})}).catch(error=>{res.json({error})})
}

exports.getTournamentPage = (req,res)=>
{
    if(!req.query.league) return res.render('leagues');
    if(!isValidObjectId(req.query.league)) return res.redirect('/leagues')
    leagueM.findById(req.query.league)
    .then(league=>
        {
            if(league) res.render('leagues')
        })
    .catch(
        res.render('leagues')
    )
}
exports.getAllLeagues = (req, res)=>
{
    leagueM.find()
    .then((leagues, err)=>
    {
        if(err) return res.json({Error:'Yea there was a problem.'});
        if(!leagues) return res.json({Error:'Yea there was a problem.'});
        if(leagues.length==0) return res.json({Error:'No leagues.'}) 
        return res.json({data:leagues})
    })
    .catch(err=>console.log('Error finding leagues.'))
}
exports.getLeague = async (req, res)=>
{
    if(!isValidObjectId(req.headers.leagueid)) return res.json({Error:'Yea this aint it chief... stop trying to mess with the system.'})
    leagueM.findById(req.headers.leagueid)
    .then((league, err)=>
    {
        if(err) return res.json({Error:'Yea there was a problem.'});
        if(!league) return res.json({Error:'Yea there was a problem.'});
        if(league.length==0) return res.json({Error:'No leagues.'}) 
        return res.json({data:league})
    })
    .catch(err=>console.log())
}
exports.getLeaguePage = (req,res)=>
{
    if(req.originalUrl == '/leagues/') return res.redirect('/leagues')

    return res.render('league')
}
exports.registerGame = async (req, res)=>
{
    const GameData = 
    {
        result:req.body.result,
        info:req.body.gameData,
        id: req.headers.id,
        leagueid: req.headers.leagueid
    }
    const data = req.body.data;
    var league = await leagueM.findById(GameData.leagueid).then(league=>{return league})
    if(!league) return res.json({Error:'The league id is not valid'})
    var Game = league.Games.filter(x=>(x.gId == GameData.id && x.Played == false));
    if(Game.length == 0) return res.json({Error: 'The game id is not valid.'})
    
    const gameStartTime = new Date(Date.now()-(GameData.info.gameTime*1000)).getTime(); //Epoch - gametime;
    const result = GameData.result;
    var update = await leagueGameM.findById(GameData.id).then(game=>{return game});
    var PA = fillPlayer(update.PA, update.PA.Name == GameData.info.PlayerA.summonerName? GameData.info.PlayerA: GameData.info.PlayerB, update.PA.Name == GameData.result.winner);
    var PB = fillPlayer(update.PB, update.PB.Name == GameData.info.PlayerA.summonerName? GameData.info.PlayerA: GameData.info.PlayerB, update.PB.Name == GameData.result.winner);
    update.PA = PA;
    update.PB = PB;
    update.Winner = result.winner;
    update.WinCondition = result.winCondition;
    update.Played = true;
    update.PlayedTimeStamp = gameStartTime;
    const updated = await leagueGameM.findByIdAndUpdate(GameData.id,update).then(()=>
    {
        return true
    })
    .catch(()=>
    {
        return false
    })
    if(!updated) return res.json({Error:'The game failed to register.'})
    var _PA = update.PA;
    _PA.extraData= update.PA.Name == GameData.info.PlayerA.summonerName? GameData.info.PlayerA: GameData.info.PlayerB;
    var _PB = update.PB;
    _PB.extraData = update.PA.Name == GameData.info.PlayerA.summonerName? GameData.info.PlayerA: GameData.info.PlayerB;
    Game[0] = {
        PA:_PA,
        PB:_PB,
        Winner:update.Winner,
        WinCondition:update.WinCondition,
        Played:true,
        PlayedTimeStamp:gameStartTime,
        gId:GameData.id
    }
    const index = league.Games.findIndex(x=>x.gId=GameData.id);
    league.Games[index] = Game[0];
    const PAIndex = league.Players.findIndex(x=>x.Name == GameData.info.PlayerA.summonerName)
    const PBIndex = league.Players.findIndex(x=>x.Name == GameData.info.PlayerB.summonerName)
    const originalA = league.Players[PAIndex];
    league.Players[PAIndex]={
        Name: originalA.Name,
        Points: originalA.Points + (originalA.Name == result.winner?1:0),
        PlayedPoints: originalA.PlayedPoints + 1,
        Kills: originalA.Kills + GameData.info.PlayerA.Kills,
        Deaths: originalA.Deaths + GameData.info.PlayerA.Deaths,
        CreepScore: originalA.CreepScore + GameData.info.PlayerA.CS
    }
    const originalB = league.Players[PBIndex];
    league.Players[PBIndex]={
        Name: originalB.Name,
        Points: originalB.Points + (originalB.Name == result.winner?1:0),
        PlayedPoints: originalA.PlayedPoints + 1,
        Kills: originalB.Kills + GameData.info.PlayerB.Kills,
        Deaths: originalB.Deaths + GameData.info.PlayerB.Deaths,
        CreepScore: originalB.CreepScore + GameData.info.PlayerB.CS
    }
    const leagueUpdated = await leagueM.findByIdAndUpdate(GameData.leagueid,league).then(()=>
    {
        return true;
    })
    .catch(()=>{return false})
    if(!leagueUpdated) return res.json({Error:'Failed to update league.'})
    res.json({success:true})
}
function fillPlayer(P, data,winner)
{
    P.winner = winner;
    P.Kills = data.Kills;
    P.Deaths = data.Deaths;
    P.CS = data.CS
    return P;
}
exports.getTournamentListenerPage = (req,res)=>
{
    return res.render('leaguelistener')
}

exports.createLeague = async (req, res)=>
{
    const verification = await verifyBody(req.body);
    if(verification.Error) return res.json(verification)
    var LeagueReference = 
    {
        Name:verification.name,
        Players:[],
        Games:[],
        Owner:verification.owner
    }
    for(player of verification.players)
    {
        (async (player)=>
        {
            LeagueReference.Players.push(
                {
                    Name:player,
                    Points:0,
                    PlayedPoints:0,
                    Kills:0,
                    Deaths:0,
                    CreepScore:0
                }
            );
        })(player)
    }

    var games = await scheduler(verification.players)
    LeagueReference.Games=games;
    var league = new leagueM();
    league.Name = LeagueReference.Name;
    league.Players = LeagueReference.Players;
    league.Games = LeagueReference.Games;
    league.Owner = LeagueReference.Owner
    league.save()
    .then(leagueS=>
    {
        return res.json({Success:true, data:leagueS});
    })
    .catch(err=>
        {
            return res.json({Error:'There was an error trying to save your league, try again later.'});
        })
}
async function verifyBody(body)
{
    if(!body.owner) return {Error:'Stop trying to mess with the system.'};
    if(!body.name) return  {Error:'The league needs to be named.'};
    if(!body.players) return {Error:'The league needs to have players playing it.'}
    var players = [...body.players.split(',')];
    var playerPayload = [];
    var promises = [];
    for (var player of players)
    {
        (async (player)=>
        {
            promises.push(new Promise ((resolve)=>
            {
                fetch(`https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/${player}`,
                {
                    method: "GET",
                    headers: {
                        "X-Riot-Token": "RGAPI-fc17a511-f587-495b-ac01-80716870d46b"
                    }
                })
                .then(response=>{return response.json()})
                .then(res=>
                    {
                        if(res.status) playerPayload = {Error:'Player '+player+' does not exist.'};
                        try
                        {
                            playerPayload.push(res.name)
                        }
                        catch{}
                        resolve();
                    })
                .catch(err=>{console.log(err)})
            }))
        })(player)
    }
    var payload = await Promise.all(promises).then(() => 
    {
        if(playerPayload.Error) return playerPayload;
        return  {name:body.name, owner:body.owner,players:playerPayload}
    })
    return payload;
}
async function scheduler(players)
{
    var games = [];
    var promises = [];
    for(var player of players)
    {
        (async (player)=>
        {
            promises.push(new Promise ((resolve)=>
            {
                for(var i = players.length-1; i>=0;i--)
                {
                    (async (player2)=>
                    {
                        if(player == player2) resolve();
                        else
                        {
                            games.push({
                                PA:{
                                    Name:player,
                                    winner:false,
                                    Kills:0,
                                    Deaths:0,
                                    CS:0
                                },
                                PB:{
                                    Name:player2,
                                    winner:false,
                                    Kills:0,
                                    Deaths:0,
                                    CS:0
                                },
                                Winner:'',
                                WinCondition:'',
                                Played:false,
                                PlayedTimeStamp:'',
                                gId:''
                            })
                            resolve()
                        }
                    })(players[i])
                }
            }))
        })(player)
    }
    var aux = [];
    for await(var game of games)
    {
        (async (game)=>
        {
            promises.push(new Promise ((resolve)=>
            {
                var gameM = new leagueGameM();
                gameM.PA = game.PA;
                gameM.PB = game.PB;
                gameM.Winner = ' ';
                gameM.WinCondition = ' ';
                gameM.Played = false;
                gameM.PlayedTimeStamp = ' ';
                gameM.save().then((g)=>
                {
                    game.gId = g.id;
                    aux.push(game)
                    resolve();
                })
            }));
        })(game);
    }
        
    var payload = await Promise.all(promises).then(() => 
    {
        return  aux;
    })
    return payload;
}
exports.createLeaguePage = (req, res)=>
{
    return res.render('createLeague');
}
exports.createLeaguePage = (req, res)=>
{
    res.render('createLeague');
}

exports.getGame = (req, res)=>
{
    return res.render('game')
}
exports.getGameData = (req, res)=>
{
    //TODO: getGameData
    //TODO: send as json
}