/*
 * Copyright 2018 John Grosh (john.a.grosh@gmail.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jagrosh.vortex.automod;

import com.jagrosh.vortex.utils.FixedCache;
import java.io.IOException;
import java.time.OffsetDateTime;
import java.util.Collections;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Invite;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.internal.entities.InviteImpl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author John Grosh (john.a.grosh@gmail.com)
 */
public class InviteResolver
{
    private final static String[] BASE_URLS = {"discordapp.com", "ptb.discordapp.com", "canary.discordapp.com", 
        /*"discord.com",*/ "ptb.discord.com", "canary.discord.com", "discord.co", "watchanimeattheoffice.com", "discord.new"};
    private final Logger log = LoggerFactory.getLogger(InviteResolver.class);
    private final FixedCache<String,Long> cached = new FixedCache<>(5000);
    private final OkHttpClient client = new OkHttpClient.Builder().build();
    
    public Invite resolveFull(String code, JDA jda)
    {
        return resolveFull(code, jda.getToken());
    }
    
    public Invite resolveFull(String code, String token)
    {
        log.debug("Attempting to resolve " + code);
        try
        {
            return getInvite(code, token);
        }
        catch(Exception ex)
        {
            return null;
        }
    }
    
    public long resolve(String code, JDA jda)
    {
        return resolve(code, jda.getToken());
    }
    
    public long resolve(String code, String token)
    {
        log.debug("Attempting to resolve " + code);
        if(cached.contains(code))
            return cached.get(code);
        try
        {
            long guildId = getInviteGuild(code, token);
            //long guildId = Invite.resolve(jda, code).complete(false).getGuild().getIdLong();
            cached.put(code, guildId);
            return guildId;
        }
        catch(Exception ex)
        {
            cached.put(code, 0L);
            return 0L;
        }
    }
    
    private long getInviteGuild(String code, String token) throws IOException
    {
        JSONObject json = getInviteJSON(code, token);
        if(json != null)
            return MiscUtil.parseSnowflake(json.getJSONObject("guild").getString("id"));
        return 0L;
    }
    
    private InviteImpl getInvite(String code, String token) throws IOException
    {
        JSONObject json = getInviteJSON(code, token);
        if(json != null)
        {   
            Invite.Channel channel;
            if(json.has("channel"))
            {
                JSONObject c = json.getJSONObject("channel");
                channel = new InviteImpl.ChannelImpl(c.optLong("id"), c.optString("name"), c.optInt("type") == 0 ? ChannelType.TEXT : ChannelType.UNKNOWN);
            }
            else
                channel = null;
            Invite.Guild guild;
            if(json.has("guild"))
            {
                JSONObject g = json.getJSONObject("guild");
                guild = new InviteImpl.GuildImpl(g.optLong("id"), g.optString("icon"), g.optString("name"), g.optString("splash"), Guild.VerificationLevel.UNKNOWN, -1, -1, Collections.emptySet());
            }
            else
                guild = null;
            return new InviteImpl(null, code, false, null, 0, 0, false, OffsetDateTime.now(), 0, channel, guild, null, null, Invite.InviteType.GUILD);
        }
        return null;
    }
    
    private JSONObject getInviteJSON(String code, String token) throws IOException
    {
        Response res = client.newCall(new Request.Builder().get()
                .addHeader("Authorization", token)
                .addHeader("User-Agent", "DiscordBot (FryeInviteResolverBasedOnVortex, 1.0)")
                .url("https://" + rand(BASE_URLS) + "/api/invites/" + code)
                .build()).execute();
        if(res.isSuccessful())
        {
            return new JSONObject(res.body().string());
        }
        
        // if it failed, log why (usually unknown invite or cloudflare issue)
        try
        {
            String message = new JSONObject(res.body().string()).getString("message");
            log.warn(String.format("Unsuccessful resolving '%s': %s", code, message));
        }
        catch(Exception ex)
        {
            log.warn(String.format("Unsuccessful resolving '%s' | Headers: %s | Body: %s", code, 
                    res.headers().toMultimap().toString(), res.body() == null ? null : res.body().string()));
        }
        return null;
    }
    
    private <T> T rand(T[] items)
    {
        return items[(int)(Math.random() * items.length)];
    }
}
