// Required Things
const Discord = require ( "discord.js" );
const config = require ( "./config.json" );
const AntiSpam = require ( "discord-anti-spam" );
const mySQL = require( "mysql2" );


// new Discord Client
const client = new Discord.Client ( {partials: [ 'MESSAGE', 'CHANNEL' ], disableMentions: "none"} );

const antiSpam = new AntiSpam ( {
    warnThreshold: 4, // Amount of messages sent in a row that will cause a warning.
    muteThreshold: 6, // Amount of messages sent in a row that will cause a mute
    kickThreshold: 10, // Amount of messages sent in a row that will cause a kick.
    banThreshold: 15, // Amount of messages sent in a row that will cause a ban.
    maxInterval: 2300, // Amount of time (in milliseconds) in which messages are considered spam.
    warnMessage: '{@user}, du hast eine Verwarnung für Spaming erhalten.', // Message that will be sent in chat upon warning a user.
    kickMessage: '**{user_tag}** wurde wegen Spaming von unserem Server gekickt.', // Message that will be sent in chat upon kicking a user.
    muteMessage: '**{user_tag}** wurde wegen Spaming temporär stumm geschaltet.',// Message that will be sent in chat upon muting a user.
    banMessage: '**{user_tag}** wurde wegen Spaming von unserer Community ausgeschlossen.', // Message that will be sent in chat upon banning a user.
    maxDuplicatesWarning: 5, // Amount of duplicate messages that trigger a warning.
    maxDuplicatesKick: 10, // Amount of duplicate messages that trigger a warning.
    maxDuplicatesBan: 15, // Amount of duplicate messages that trigger a warning.
    exemptPermissions: [ 'ADMINISTRATOR' ], // Bypass users with any of these permissions.
    ignoreBots: true, // Ignore bot messages.
    verbose: true, // Extended Logs from module.
    ignoredUsers: [], // Array of User IDs that get ignored.
    muteRoleName: "Muted", // Name of the role that will be given to muted users!
    removeMessages: true // If the bot should remove all the spam messages when taking action on a user!
    // And many more options... See the documentation.
} );

const con = mySQL.createConnection ( {
    host: config.mysqlHost,
    user: config.mysqlUsername,
    password: config.mysqlPassword,
    port: config.mysqlPort,
    database: config.mysqlDatabase
} );

let badWords = {}; // 0 => {1:2,}
let error = false;

con.connect(function(err) {
    if (err) {
        console.log ( "[ERROR] Cannot connect to database! Critical error" );
        process.exit();
        return;
    }
    console.log("[INFO] Connected to mySQL Server!");
});

client.on ( "ready", () =>
{
    console.log ( "[INFO] Bot is now ready" );
    client.user.setPresence ( {
        activity:
            {
                name: "Reading your lovely messages",
                type: 'PLAYING'
            }
    } ).then ( r =>
    {
        console.log ( "[INFO] Presence set" );


    } );


    if( loadAllUp() === false )
    {

        client.user.setPresence ( {
            activity:
                {
                    name: "Error 0",
                    type: 'PLAYING'
                }
        } ).then ( r =>
        {
            console.log ( "[INFO] Error 0 presence shown" );
            error = true;

        } );
    }

} );


client.on ( "message", async ( message ) =>
{
    if ( message.author.bot ) return;

    // Beleidigungsfilter?
    if ( !message.content.startsWith ( config.prefix ) )
    {
        // Check if the user has Admin permissions, so skip him
        if ( message.member.hasPermission ( "ADMINISTRATOR" ) ) return;

        // Filter goes here

        if ( await checkMessage ( message.guild.id, message.content ) )
        {
            await message.delete ();
            let notifyChannelID = await getNotifyChannel ( message.guild.id );

            if ( notifyChannelID.length < 1 || notifyChannelID === "" || notifyChannelID === null )
            {
                // NotifyChannel not send, so we gonna send the msg here
                const notify = new Discord.MessageEmbed ()
                    .setColor ( "#b30202" )
                    .setTitle ( "Nachricht gelöscht" )
                    .setDescription ( `Es wurde eine Nachricht von ${ message.author.toString () } entfernt, da ein dort enthaltenes Wort auf der Blacklist steht.\n\nMir wurde noch nicht gesagt, wo ich Nachricht wie diese hinschreiben soll.\nDu kannst dies als Administrator mit ".w setNotify" in einem beliebigen Channel setzen.` )

                message.channel.send( `**Nachricht gelöscht!**\nEs wurde eine Nachricht von ${ message.author.toString () } entfernt, da ein dort enthaltenes Wort auf der Blacklist steht.\n\nÜbrigens, mir wurde noch nicht gesagt, wo ich Nachricht wie diese hinschreiben soll.\nDu kannst dies als Administrator mit ".w setNotify" in einem beliebigen Channel setzen.` ).then ( () =>
                {
                } ).catch ( reason =>
                {
                    console.log ( "[ERROR] Cannot send a message on guild " + message.guild.id + " in channel " + message.channel.id + " " + reason.message );
                } );
            } else
            {
               
                const notifyChannel = await message.member.guild.channels.cache.find ( ch => ch.id === notifyChannelID );
                const notify = new Discord.MessageEmbed ()
                    .setColor ( "#b30202" )
                    .setTitle ( "Nachricht gelöscht" )
                    .setDescription ( `Es wurde eine Nachricht von ${ message.author.toString () } entfernt, da ein dort enthaltenes Wort auf der Blacklist steht.` );
                notifyChannel.send ( `Nachricht gelöscht!\nEs wurde eine Nachricht von ${ message.author.toString () } entfernt, da ein dort enthaltenes Wort auf der Blacklist steht.` ).then ( () =>
                {
                } ).catch ( () =>
                {
                    console.log ( "[ERROR] Cannot send a message on guild " + message.guild.id + " in channel " + message.channel.id );
                } );

            }
        }


        return;
    }

    // Commands here

    if ( !message.member.hasPermission ( "ADMINISTRATOR" ) )
    {
        await message.reply ( "mir wurde beigebracht, dass ich mit fremden Leuten nicht reden darf :c" );
        return;
    }
    let notifyChannelID = await getNotifyChannel ( message.guild.id );

    const commandBody = message.content.slice ( config.prefix.length );
    const args = commandBody.split ( ' ' ); // 1=> arg
    const command = args.shift ().toLowerCase ();


    switch( command )
    {
        case "p":
        case "ping":
            const timeTaken = Date.now() - message.createdTimestamp;
            message.reply(`Der Ping von mir liegt bei ${timeTaken}ms.`).then(() => {
            });
            break;
        case "auflisten":
        case "liste":
        case "filter":
        case "show":
        case "list":
            // Listet alle Wörter des Servers mit indexen auf
            let words = listWords( message.guild.id );
            if( words === false )
            {
                await message.reply( `ich konnte deinen Filter nicht abrufen! - Bitte versuche es später erneut :c` );
                return;
            }
            let output = "";

            for ( let wordsKey in words )
            {
                output += `__${wordsKey}__\t | \t__${words[wordsKey]}__\n`;
            }


            await message.channel.send ( `Folgende Wörter & Sätze stehen auf der Schwarzen Liste:\nIndex\tWort\n` + output );
            break;
        case "remove":
        case "delete":
        case "del":
            if ( args.length > 0)
            {
                const index = Number.parseInt ( args[0] );
                if ( !isNaN ( index ) )
                {
                    if( await deleteWord(message.guild.id, args[0]) === true )
                    {
                        await message.reply( `ich habe Index erfolgreich gelöscht! :D` );
                    }
                    else
                    {
                        await message.reply( `ich konnte den Index nicht entfernen. Hast du dich vielleicht vertippt?` );
                    }
                } else
                {
                    await message.reply ( `bitte gebe einen Index mit an. ${ config.prefix }delete <index>` )
                }
            } else
            {
                await message.reply ( `bitte gebe einen Index mit an. ${ config.prefix }delete <index>` )
            }
            break;
        case "add":
        case "hinzufügen":
        case "addword":
            if ( args.length > 0 )
            {
                if( await addWord(message.guild.id, args[0]) )
                {
                    await message.reply( `ich habe das Wort oder den Satz erfolgreich zu deinem Filter hinzugefügt! :D` );
                }
                else
                {
                    await message.reply( `Oopsie! Ich konnte den Filter nicht verändern - Wende dich bitte an den Support!` );
                }

            } else
            {
                await message.reply ( `bitte gebe ein Wort/Satz mit an. ${ config.prefix }add <Wort/Satz>` );
            }
            break;
        case "setnotifychannel":
        case "setnotify":
        case "setchannel":
            await setNotifyChannel( message.guild.id, message.channel.id );
            await message.reply( `der neue Notify-Channel ist nun dieser hier!` );
            break;
        case "clear":
        case "c":
            await message.reply(`Befehl derzeit nicht aktiv!`);
            break;
            async function clear() {
                let fetched;
                do {
                    fetched = await message.channel.messages.fetch({limit: 100});
                    await message.channel.messages.bulkDelete(fetched);
                }
                while(fetched.size >= 2);
            }
            clear().then( () => { message.reply( `es wurden alle Nachrichten gelöscht!` ) } )
            break;
        case "?":
        case "help?":
        case "help":
        case "h":
        case "hilfe":
            let help = `**Hilfe**\nMein Prefix ist .w, setze dies einfach vor jeden Befehl!\n\nadd <Word/Sentence> - Fügt ein Wort/Satz zum Filter hinzu\ndelete <Index> - Löscht ein Wort/Satz\nlist - Listet alle Wörter auf\nsetnotify - Setzt den Notify-Channel auf den aktuellen Kanal`;

            await message.channel.send( help );
            break;
        case "author":
            await message.reply( `mein Vater ist Tobstr#7626` );
            break;
        case "idiot":
        case "maul":
        case "hurensohn":
        case "nein":
        case "support":
            await message.reply( `schreibe meinem Vater Tobstr#7627 am besten eine Privat-Nachricht wenn ich nicht richtig gehandelt habe :c` );
            break;
        default:
            await message.reply( `den Befehl kenne ich leider nicht :c - .w help?` );
            break;

    }


} );

client.on ( "guildCreate", ( guild ) =>
{
    console.log ( `[INFO] Joined a new guild named "${ guild.name }" with ID ${ guild.id }` );
    createServerEntry(guild.id, guild.name).then();

} );

async function createServerEntry( a_ServerID, a_ServerName )
{
    console.log ( `[INFO] Creating server entry...` );
    let query = "INSERT INTO guilds ( guild_id, server_name ) VALUES ( ?, ? )";

    await con.promise().query( query, [a_ServerID, a_ServerName] );


    return true;

}


function checkMessage( a_ServerID, a_Message )
{
    /*let query = "SELECT word FROM words WHERE word LIKE ?";
    con.query( query, ['%' + a_Message + '%'], function( error, results, fields ) {
        if( error )
        {
            console.log ( `[ERROR] An error occurred while fetching th` )
        }
    } );*/
    for (const badWordsKey in badWords[a_ServerID] ) {
        if( a_Message.includes( badWords[a_ServerID][badWordsKey] ) )
        {
            return true;
        }
    }

    return false;
}

async function refreshWords( a_ServerID )
{

    console.log( "[INFO] Loading filter for guild " + a_ServerID )
    // Add the word to the database
    let query = "SELECT id, word FROM words WHERE guildID = ?";
    let [results] = await con.promise().query( query, [a_ServerID] );


    //results = results[0];
    badWords[a_ServerID] = {};
    for (let i = 0; i < results.length; i++ ) {
        badWords[a_ServerID][results[i].id] = results[i].word;
    }

    return true;

}

async function loadAllUp()
{

    console.log( "[INFO] Loading all up..." );
    // LOADING ALL UP, can take a while
    // Add the word to the database
    let query = "SELECT id, guild_id FROM guilds";


    let [results] = await con.promise().query( query );

    for (let i = 0; i < results.length; i++) {
        await refreshWords(results[i].guild_id);
    }
    return true;
}

async function getNotifyChannel( a_ServerID )
{
    let query = "SELECT id, notify_channel FROM guilds WHERE guilds.guild_id = ?";
    let [results] = await con.promise().query( query, [ a_ServerID ] );



    if( results.length < 1 )
    {
        console.log( "newentry")
        await createServerEntry(a_ServerID);
        return await getNotifyChannel( a_ServerID );
    }

    if( results[0]['notify_channel'] === null )
    {
        return null;
    }
    return await results[0]['notify_channel'].toString();

}

async function setNotifyChannel( a_ServerID, a_ChannelID )
{
    let query = "UPDATE guilds SET notify_channel = ? WHERE guild_id = ?";
    let [results] = await con.promise().query( query, [ a_ChannelID, a_ServerID ] );

    return true;
}

// function broken i guess, the console.log is not getting out, so idk what happening
async function deleteWord( a_ServerID, a_Index )
{
    // Delete the word out of the database
    // Add the word to the database
    let success = false;
    let query = "DELETE FROM words WHERE id = ? AND guildID = ?";
    let [results] = await con.promise().query( query, [a_Index, a_ServerID] );

    await refreshWords(a_ServerID);
    return true;

}

async function addWord( a_ServerID, a_Word )
{
    let success = false;
    // Add the word to the database
    let query = "INSERT INTO words ( word, guildID ) VALUES ( ?, ? )";
    let [results] = await con.promise().query( query, [ a_Word, a_ServerID ] );

    await refreshWords(a_ServerID);
    return true;

}

function listWords( a_ServerID )
{
    return badWords[a_ServerID];
}


// Discord Login

client.login ( config.TOKEN ).then ( r =>
{
    console.log ( "[INFO] Logged in as Woam" )
} );

