package net.taehui.twilight.system

import com.fasterxml.jackson.databind.ObjectMapper
import net.dv8tion.jda.api.JDA
import net.dv8tion.jda.api.JDA.Status
import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
import net.dv8tion.jda.api.events.guild.member.GuildMemberRemoveEvent
import net.dv8tion.jda.api.events.guild.member.GuildMemberUpdateEvent
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateAvatarEvent
import net.dv8tion.jda.api.events.message.MessageDeleteEvent
import net.dv8tion.jda.api.events.message.MessageReceivedEvent
import net.dv8tion.jda.api.events.message.MessageUpdateEvent
import net.dv8tion.jda.api.hooks.ListenerAdapter
import net.dv8tion.jda.api.requests.GatewayIntent
import net.taehui.twilight.*
import org.apache.hc.client5.http.classic.methods.HttpGet
import org.apache.hc.client5.http.impl.classic.HttpClients
import java.text.NumberFormat
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap

object PlatformSystem : Logger {
    class Client {
        var platform = ""
        var qwilight = 0L
        var siteComment = 0L
        var siteDefault = 0L
        var sitePlatform = 0L
        var siteAudio = 0L
        var siteYellDefault = ""
        var siteYellEnter = ""
        var siteYellQuit = ""
        var siteYellTaehui = ""
        var siteYellComment = ""
        var siteYellAbility = ""
        var siteYellAbilityMini = ""
        var siteYellLevel = ""
        var siteYellInvite = ""
        var siteYellTV = ""
    }

    private val drawingStore = ConcurrentHashMap<String, ByteArray>()
    private val platformClient: Client = ObjectMapper().readValue(
        Twilight::class.java.classLoader.getResourceAsStream("Client.json"),
        Client::class.java
    )
    private var platform: JDA? = null
    private var qwilightPlatform: Guild? = null
    var platformAvatars = emptyList<Member>()

    fun handleSystem() {
        if (Configure.mode.platform) {
            try {
                fun doCallPlatformAvatars() {
                    qwilightPlatform?.loadMembers()?.onSuccess { avatars ->
                        platformAvatars = avatars.filter { !it.user.isBot && !it.user.isSystem }
                        CompletableFuture.allOf(
                            *platformAvatars.map { putDrawing("$${it.id}", it.effectiveAvatarUrl) }.toTypedArray()
                        )
                    }?.onSuccess {
                        SiteHandler.doCallSiteAvatar(
                            SiteHandler.platformSiteID,
                            platformAvatars
                        )
                    }
                }

                platform = JDABuilder.createDefault(platformClient.platform)
                    .enableIntents(GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_MEMBERS)
                    .addEventListeners(object : ListenerAdapter() {
                        override fun onMessageReceived(event: MessageReceivedEvent) {
                            event.member?.let {
                                if (!event.author.isBot && !event.author.isSystem) {
                                    if (event.channel.idLong == platformClient.siteDefault) {
                                        event.message.delete().queue()
                                        SiteHandler.putSiteYell(SiteHandler.defaultSiteID, it, event)
                                    } else if (event.channel.idLong == platformClient.sitePlatform) {
                                        SiteHandler.putSiteYell(SiteHandler.platformSiteID, it, event)
                                    }
                                }
                            }
                        }

                        override fun onMessageUpdate(event: MessageUpdateEvent) {
                            if (event.channel.idLong == platformClient.sitePlatform) {
                                event.member?.let {
                                    SiteHandler.doModifySIteYell(SiteHandler.platformSiteID, it, event)
                                }
                            }
                        }

                        override fun onMessageDelete(event: MessageDeleteEvent) {
                            if (event.channel.idLong == platformClient.sitePlatform) {
                                SiteHandler.wipeSiteYell(SiteHandler.platformSiteID, event)
                            }
                        }

                        override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
                            doCallPlatformAvatars()
                        }

                        override fun onGuildMemberRemove(event: GuildMemberRemoveEvent) {
                            doCallPlatformAvatars()
                        }

                        override fun onGuildMemberUpdateAvatar(event: GuildMemberUpdateAvatarEvent) {
                            doCallPlatformAvatars()
                        }

                        override fun onGuildMemberUpdate(event: GuildMemberUpdateEvent) {
                            doCallPlatformAvatars()
                        }
                    }).build().awaitReady()
                qwilightPlatform = platform?.getGuildById(platformClient.qwilight)
                doCallPlatformAvatars()

                qwilightPlatform?.audioManager?.openAudioConnection(qwilightPlatform?.getVoiceChannelById(platformClient.siteAudio))
            } catch (e: Throwable) {
                logFault(e)
            }
        }
    }

    val platformStatus: String
        get() = (platform?.status ?: Status.SHUTDOWN).toString()

    fun dispose() {
        platform?.shutdown()
    }

    fun sendSiteYell(siteYellData: JSON.TwilightSiteYell) {
        try {
            var site: TextChannel? = null
            when (UUID.fromString(siteYellData.siteID)) {
                SiteHandler.commentSiteID -> site = qwilightPlatform?.getTextChannelById(platformClient.siteComment)
                SiteHandler.defaultSiteID -> site = qwilightPlatform?.getTextChannelById(platformClient.siteDefault)
            }

            when (siteYellData.avatarName) {
                "@Enter" -> site?.sendMessage(String.format(platformClient.siteYellEnter, siteYellData.siteYell))

                "@Quit" -> site?.sendMessage(String.format(platformClient.siteYellQuit, siteYellData.siteYell))

                "@Notify" -> site?.sendMessage(
                    String.format(
                        platformClient.siteYellTaehui,
                        siteYellData.siteYell
                    )
                )

                "@Comment" -> {
                    val twilightCommentSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightCommentSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellComment,
                            twilightCommentSiteYell.avatarName,
                            twilightCommentSiteYell.levelText,
                            twilightCommentSiteYell.artist,
                            twilightCommentSiteYell.title,
                            if (twilightCommentSiteYell.genre.isEmpty()) "" else "#${twilightCommentSiteYell.genre}",
                            NumberFormat.getInstance().format(twilightCommentSiteYell.stand.toLong())
                        )
                    )
                }

                "@Ability" -> {
                    val twilightAbilitySiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightAbilitySiteYell::class.java)
                    val inputMode = when (twilightAbilitySiteYell.inputMode) {
                        Component.InputMode.INPUT_MODE_5_1 -> "⑤K"
                        Component.InputMode.INPUT_MODE_7_1 -> "⑦K"
                        Component.InputMode.INPUT_MODE_9 -> "9K"
                        else -> ""
                    }
                    val ability = twilightAbilitySiteYell.ability
                    site?.sendMessage(
                        if (ability < 0.01) String.format(
                            platformClient.siteYellAbilityMini,
                            twilightAbilitySiteYell.avatarName,
                            inputMode
                        ) else String.format(
                            platformClient.siteYellAbility,
                            twilightAbilitySiteYell.avatarName,
                            inputMode,
                            ability
                        )
                    )
                }

                "@Level" -> {
                    val twilightLevelSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightLevelSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellLevel,
                            twilightLevelSiteYell.avatarName,
                            twilightLevelSiteYell.title
                        )
                    )
                }

                "@Invite" -> {
                    val twilightInviteSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightInviteSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellInvite,
                            twilightInviteSiteYell.avatarName,
                            twilightInviteSiteYell.siteName
                        )
                    )
                }

                "@TV" -> {
                    val twilightTVSiteYell =
                        ObjectMapper().readValue(siteYellData.siteYell, JSON.TwilightTVSiteYell::class.java)
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellTV,
                            twilightTVSiteYell.text,
                            twilightTVSiteYell.title
                        )
                    )
                }

                else -> {
                    site?.sendMessage(
                        String.format(
                            platformClient.siteYellDefault,
                            siteYellData.avatarName,
                            siteYellData.siteYell
                        )
                    )
                }
            }?.queue()
        } catch (e: Throwable) {
            logFault(e)
        }
    }

    fun putDrawing(avatarID: String, platformDrawing: String): CompletableFuture<Void> {
        return if (drawingStore.containsKey(avatarID)) {
            CompletableFuture.completedFuture(null)
        } else {
            logFuture {
                HttpClients.createDefault()
                    .use {
                        drawingStore[avatarID] = it.execute(HttpGet(platformDrawing), HCDataHandler())
                    }
            }
        }
    }

    fun getDrawing(avatarID: String): ByteArray? {
        return drawingStore[avatarID]
    }
}