package com.avairebot.orion.database.controllers;

import com.avairebot.orion.Constants;
import com.avairebot.orion.Orion;
import com.avairebot.orion.cache.CacheType;
import com.avairebot.orion.database.collection.DataRow;
import com.avairebot.orion.database.transformers.GuildTransformer;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.TextChannel;

import javax.annotation.CheckReturnValue;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GuildController {

    private static final String CACHE_STRING = "database.guilds.%s";

    @CheckReturnValue
    public static GuildTransformer fetchGuild(Orion orion, Message message) {
        if (!message.getChannelType().isGuild()) {
            return null;
        }

        return fetchGuild(orion, message.getGuild());
    }

    @CheckReturnValue
    public static GuildTransformer fetchGuild(Orion orion, Guild guild) {
        if (isCached(orion, guild.getId())) {
            return (GuildTransformer) orion.getCache().getAdapter(CacheType.MEMORY).get(
                String.format(CACHE_STRING, guild.getId())
            );
        }

        try {
            GuildTransformer transformer = new GuildTransformer(orion.getDatabase().newQueryBuilder(Constants.GUILD_TABLE_NAME)
                .select("guild_types.name as type_name", "guild_types.limits as type_limits", "guilds.*")
                .leftJoin("guild_types", "guilds.type", "guild_types.id")
                .where("guilds.id", guild.getId())
                .get().first());

            if (!transformer.hasData()) {
                final String cacheToken = String.format(CACHE_STRING, guild.getId());
                try {
                    orion.getDatabase().newQueryBuilder(Constants.GUILD_TABLE_NAME)
                        .insert(statement -> {
                            statement.set("id", guild.getId())
                                .set("owner", guild.getOwner().getUser().getId())
                                .set("name", guild.getName(), true)
                                .set("channels_data", buildChannelData(guild.getTextChannels()), true);

                            if (guild.getIconId() != null) {
                                statement.set("icon", guild.getIconId());
                            }

                            orion.getCache().getAdapter(CacheType.MEMORY)
                                .put(cacheToken, new GuildTransformer(new DataRow(statement.getItems())), 2);
                        });
                } catch (Exception ex) {
                    Orion.getLogger().error(ex.getMessage(), ex);
                }

                return (GuildTransformer) orion.getCache().getAdapter(CacheType.MEMORY).get(cacheToken);
            }

            orion.getCache().getAdapter(CacheType.MEMORY).put(String.format(CACHE_STRING, guild.getId()), transformer, 300);

            return transformer;
        } catch (SQLException ex) {
            Orion.getLogger().error(ex.getMessage(), ex);
            return null;
        }
    }

    private static boolean isCached(Orion orion, String guildId) {
        return orion.getCache().getAdapter(CacheType.MEMORY).has(
            String.format(CACHE_STRING, guildId)
        );
    }

    private static String buildChannelData(List<TextChannel> textChannels) {
        List<Map<String, Object>> channels = new ArrayList<>();
        for (TextChannel channel : textChannels) {
            Map<String, Object> item = new HashMap<>();

            item.put("id", channel.getId());
            item.put("name", channel.getName());
            item.put("position", channel.getPosition());

            channels.add(item);
        }
        return Orion.GSON.toJson(channels);
    }
}

@@ -191,35 +191,170 @@
 ion.
-database.collection.DataRow
+contracts.database.transformers.Transformer;%0Aimport com.avairebot.orion.database.collection.DataRow;%0Aimport com.avairebot.orion.database.query.ChangeableStatement
 ;%0Aim
@@ -3039,32 +3039,251 @@
         
-orion.getCache()
+// If the guild haven't been encoded yet, we'll do it below.%0A            String name = transformer.getRawData().get(%22name%22).toString();%0A            if (name.startsWith(%22base64:%22)) %7B%0A                orion.getCache()%0A                    
 .getAdap
@@ -3303,16 +3303,37 @@
 .MEMORY)
+%0A                    
 .put(Str
@@ -3389,24 +3389,745 @@
 mer, 300);%0A%0A
+                return transformer;%0A            %7D%0A%0A            orion.getDatabase().newQueryBuilder(Constants.GUILD_TABLE_NAME)%0A                .where(%22id%22, guild.getId())%0A                .update(statement -%3E %7B%0A                    statement.set(%22name%22, guild.getName(), true);%0A                    statement.set(%22channels_data%22, buildChannelData(guild.getTextChannels()), true);%0A%0A                    setUpdateStatementFor(transformer, statement, %22channels%22);%0A                    setUpdateStatementFor(transformer, statement, %22claimable_roles%22);%0A                    setUpdateStatementFor(transformer, statement, %22prefixes%22);%0A                    setUpdateStatementFor(transformer, statement, %22aliases%22);%0A                %7D);%0A%0A
             
@@ -4921,24 +4921,24 @@
 ;%0A        %7D%0A
-
         retu
@@ -4974,11 +4974,290 @@
 );%0A    %7D
+%0A%0A    private static void setUpdateStatementFor(Transformer transformer, ChangeableStatement statement, String name) %7B%0A        if (transformer.getRawData().get(name, null) != null) %7B%0A            statement.set(name, transformer.getRawData().getString(name), true);%0A        %7D%0A    %7D
 %0A%7D%0A

Add encoder to guild controller if data is not already encoded