package net.taehui.twilight.system

import com.google.protobuf.ByteString
import net.dv8tion.jda.api.entities.Member
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.taehui.twilight.Avatar
import net.taehui.twilight.JSON
import net.taehui.twilight.Logger
import net.taehui.twilight.Site
import java.util.*
import java.util.concurrent.ConcurrentHashMap

object SiteHandler {
    private val sites = ConcurrentHashMap<UUID, Site>()

    val commentSiteID: UUID = UUID.fromString("00000000-0000-0000-0000-000000000000")
    val defaultSiteID: UUID = UUID.fromString("00000000-0000-0000-0000-000000000001")
    val platformSiteID: UUID = UUID.fromString("00000000-0000-0000-0000-000000000002")

    init {
        val defaultSite = Site(
            defaultSiteID, "@Default",
            isEditable = true,
            isGetNotify = true,
            isEssential = true,
            hasAvatar = true
        )
        defaultSite.setSiteYells(DB.getSiteYells(defaultSiteID.toString()))
        sites[defaultSiteID] = defaultSite
        val commentSite = Site(
            commentSiteID, "@Comment",
            isEditable = false,
            isGetNotify = false,
            isEssential = true,
            hasAvatar = true
        )
        commentSite.setSiteYells(DB.getSiteYells(commentSiteID.toString()))
        sites[commentSiteID] = commentSite
        if (Configure.mode.platform) {
            val platformSite = Site(
                platformSiteID, "@Platform",
                isEditable = false,
                isGetNotify = false,
                isEssential = true,
                hasAvatar = false
            )
            platformSite.setSiteYells(DB.getSiteYells(platformSiteID.toString()))
            sites[platformSiteID] = platformSite
        }
    }

    fun send(siteID: UUID, event: EventOuterClass.Event) {
        sites[siteID]?.send(event)
    }

    fun silentSite(siteID: UUID) {
        DB.silentSite(siteID.toString()).thenAccept {
            sites[siteID]?.silentSite()
        }
    }

    fun wipeSite(siteID: UUID) {
        sites[siteID]?.let {
            if (it.quitAvatars()) {
                sites.remove(siteID)
            } else {
                silentSite(siteID)
            }
        }
    }

    fun doInfo(logger: Logger) {
        sites.values.forEach { logger.logInfo(it.toString()) }
    }

    fun putSite(siteName: String) {
        val siteID = UUID.randomUUID()
        val site = Site(
            siteID, siteName,
            isEditable = true,
            isGetNotify = true,
            isEssential = false,
            hasAvatar = true
        )
        sites[siteID] = site
    }

    fun hasAvatar(avatar: Avatar, siteID: UUID): Boolean {
        return sites[siteID]?.isAvatarJoined(avatar) == true
    }

    fun isNetSiteHand(avatar: Avatar, siteID: UUID): Boolean {
        return sites[siteID]?.isNetSiteHand(avatar) == true
    }

    fun getCalledSites(): Collection<Any> {
        return sites.values.mapNotNull { it.getCallingData() }
    }

    fun setLevying(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setLevying(avatar)
    }

    fun stopSiteNet(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.stopSiteNet(avatar)
    }

    fun setCompiled(avatar: Avatar, toQwilightCompiled: JSON.QwilightCompiled) {
        sites[UUID.fromString(toQwilightCompiled.siteID)]?.setCompiled(
            avatar,
            toQwilightCompiled.isCompiled,
            toQwilightCompiled.handlerID
        )
    }

    fun setModeComponent(avatar: Avatar, qwilightSetModeComponent: JSON.QwilightSetModeComponent) {
        sites[UUID.fromString(qwilightSetModeComponent.siteID)]?.setModeComponent(
            avatar, qwilightSetModeComponent.data
        )
    }

    fun setFavorNoteFile(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setFavorNoteFile(
            avatar
        )
    }

    fun setFavorModeComponent(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setFavorModeComponent(
            avatar
        )
    }

    fun setFavorAudioMultiplier(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setFavorAudioMultiplier(
            avatar
        )
    }

    fun setAvatarGroup(avatar: Avatar, siteID: UUID, qwilightSetAvatarGroup: JSON.QwilightSetAvatarGroup) {
        sites[siteID]?.setAvatarGroup(
            avatar, qwilightSetAvatarGroup
        )
    }

    fun setAutoSiteHand(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setAutoSiteHand(
            avatar
        )
    }

    fun setValidHunterMode(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.setValidHunterMode(
            avatar
        )
    }

    fun setValidNetMode(avatar: Avatar, siteID: UUID, qwilightSetValidNetMode: JSON.QwilightSetValidNetMode) {
        sites[siteID]?.setValidNetMode(
            avatar, qwilightSetValidNetMode
        )
    }

    fun setAllowedPostableItems(avatar: Avatar, siteID: UUID, qwilightSetAllowedPostableItems: JSON.QwilightSetAllowedPostableItems) {
        sites[siteID]?.setAllowedPostableItems(
            avatar, qwilightSetAllowedPostableItems
        )
    }

    fun doCallNetSiteComments(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.doCallNetSiteComments(
            avatar
        )
    }

    fun doCallSiteAvatar(siteID: UUID, avatars: List<Member>) {
        sites[siteID]?.doCallSiteAvatar(null, avatars)
    }

    fun setSiteHand(avatar: Avatar, siteID: UUID, siteHandAvatar: Avatar) {
        sites[siteID]?.setSiteHand(
            avatar, siteHandAvatar
        )
    }

    fun setSiteName(avatar: Avatar, siteID: UUID, siteName: String) {
        sites[siteID]?.setSiteName(
            avatar, siteName
        )
    }

    fun setNoteFileContents(avatar: Avatar, siteID: UUID, noteFileData: JSON.QwilightSetNoteFile) {
        sites[siteID]?.setNoteFileContents(
            avatar, noteFileData
        )
    }

    fun setNetData(avatar: Avatar, qwilightCallNet: EventOuterClass.Event.QwilightCallNet, comment: CommentOuterClass.Comment?) {
        sites[UUID.fromString(qwilightCallNet.siteID)]?.setNetData(
            avatar, qwilightCallNet, comment
        )
    }

    fun exileAvatar(siteID: UUID, avatar: Avatar, asAvatar: Avatar) {
        sites[siteID]?.let {
            if (it.exileAvatar(
                    avatar, asAvatar
                ) && it.isVoid
            ) {
                sites.remove(siteID)
                it.onWipeSite()
            }
        }
    }

    fun putSilentSite(avatar: Avatar, avatarSilent: Avatar) {
        val siteID = UUID.randomUUID()
        val site = Site(
            siteID,
            false,
            "${avatar.avatarName}, ${avatarSilent.avatarName}",
            "",
            avatar,
            false
        )
        site.putSiteYell("@Site", avatar.avatarID, avatar.avatarName, System.currentTimeMillis())
        sites[siteID] = site
        site.enterSite(avatar)
        site.enterSite(avatarSilent)
    }

    fun putSite(avatar: Avatar, siteID: UUID, qwilightNewSite: JSON.QwilightNewSite) {
        val site = Site(siteID, false, qwilightNewSite.siteName, qwilightNewSite.siteCipher, avatar, true)
        site.putSiteYell("@Site", avatar.avatarID, avatar.avatarName, System.currentTimeMillis())
        sites[siteID] = site
        site.enterSite(avatar)
    }

    fun putNetSite(avatar: Avatar, siteID: UUID, qwilightNewSite: JSON.QwilightNewSite) {
        val millis = System.currentTimeMillis()
        val site = Site(siteID, true, qwilightNewSite.siteName, qwilightNewSite.siteCipher, avatar, true)
        site.putSiteYell("@Net", avatar.avatarID, avatar.avatarName, millis)
        if (qwilightNewSite.siteCipher.isEmpty() && !avatar.isAwilight && hasAvatar(
                avatar,
                defaultSiteID
            )
        ) {
            putSiteYell(
                siteID,
                qwilightNewSite.siteName,
                avatar,
                millis
            )
        }
        sites[siteID] = site
        site.setModeComponent(qwilightNewSite.data!!)
        site.setNoteFileContents(JSON.QwilightSetNoteFile().apply {
            noteID = qwilightNewSite.noteID
            noteIDs = qwilightNewSite.noteIDs
            title = qwilightNewSite.title
            artist = qwilightNewSite.artist
            genre = qwilightNewSite.genre
            levelText = qwilightNewSite.levelText
            level = qwilightNewSite.level
            wantLevelID = qwilightNewSite.wantLevelID
            judgmentStage = qwilightNewSite.judgmentStage
            hitPointsValue = qwilightNewSite.hitPointsValue
            totalNotes = qwilightNewSite.totalNotes
            longNotes = qwilightNewSite.longNotes
            autoableNotes = qwilightNewSite.autoableNotes
            trapNotes = qwilightNewSite.trapNotes
            highestInputCount = qwilightNewSite.highestInputCount
            length = qwilightNewSite.length
            bpm = qwilightNewSite.bpm
            lowestBPM = qwilightNewSite.lowestBPM
            highestBPM = qwilightNewSite.highestBPM
            isAutoLongNote = qwilightNewSite.isAutoLongNote
            inputMode = qwilightNewSite.inputMode
            bundleEntryPath = qwilightNewSite.bundleEntryPath
        })
        site.setAllowedPostableItems(qwilightNewSite.allowedPostableItems)
        site.enterSite(avatar)
    }

    fun quitAvatar(avatar: Avatar) {
        sites.forEach {
            it.value.quitAvatar(avatar)
            if (it.value.isVoid) {
                sites.remove(it.key)
                it.value.onWipeSite()
            }
        }
    }

    fun quitAvatar(avatar: Avatar, siteID: UUID) {
        sites[siteID]?.let {
            it.quitAvatar(avatar)
            if (it.isVoid) {
                sites.remove(siteID)
                it.onWipeSite()
            }
        }
    }

    fun enterSite(siteID: UUID, avatar: Avatar, siteCipher: String) {
        sites[siteID]?.enterSite(
            avatar, siteCipher
        ) ?: avatar.send(EventOuterClass.Event.EventID.WARNING, avatar.translateLanguage("siteNotAvailable"))
    }

    fun setNetBundle(siteID: UUID, bundleName: String) {
        sites[siteID]?.setNetBundle(bundleName)
    }

    private fun putSiteYell(siteID: UUID, siteName: String, avatar: Avatar, millis: Long) {
        sites[defaultSiteID]?.putSiteYell(
            siteID, siteName, avatar.avatarID, avatar.avatarName, millis
        )
    }

    fun putSiteYell(tvItem: TVSystem.TVItem) {
        sites[defaultSiteID]?.putSiteYell(tvItem)
    }

    fun putSiteYell(avatar: Avatar, qwilightSiteYell: JSON.QwilightSiteYell) {
        if (qwilightSiteYell.siteYell.length <= 160 && qwilightSiteYell.siteYell.isNotBlank()) {
            sites[UUID.fromString(qwilightSiteYell.siteID)]?.putSiteYell(avatar, qwilightSiteYell)
        }
    }

    fun putSiteYell(siteID: UUID, avatar: Member, platformEvent: MessageReceivedEvent) {
        sites[siteID]?.putSiteYell(
            avatar, platformEvent
        )
    }

    fun putSiteYell(siteID: UUID, twilightCommentSiteYell: JSON.TwilightCommentSiteYell) {
        sites[siteID]?.putSIteYell(
            twilightCommentSiteYell
        )
    }

    fun putSiteYell(siteID: UUID, twilightAbilitySiteYell: JSON.TwilightAbilitySiteYell) {
        sites[siteID]?.putSIteYell(
            twilightAbilitySiteYell
        )
    }

    fun putSiteYell(siteID: UUID, twilightLevelSiteYell: JSON.TwilightLevelSiteYell) {
        sites[siteID]?.putSIteYell(
            twilightLevelSiteYell
        )
    }

    fun doModifySIteYell(siteID: UUID, avatar: Member, platformEvent: MessageUpdateEvent) {
        sites[siteID]?.doModifySiteYell(
            avatar, platformEvent
        )
    }

    fun wipeSiteYell(siteID: UUID, platformEvent: MessageDeleteEvent) {
        sites[siteID]?.wipeSiteYell(platformEvent)
    }

    fun getSiteYells(siteID: UUID, avatar: Avatar, qwilightGetSiteYells: JSON.QwilightGetSiteYells) {
        sites[siteID]?.getSiteYells(
            avatar, qwilightGetSiteYells
        )
    }

    fun setSiteNotify(siteID: UUID, input: String) {
        sites[siteID]?.setSiteNotify(
            input
        )
    }

    fun audioInput(siteID: UUID, avatar: Avatar, data: ByteString) {
        sites[siteID]?.audioInput(
            avatar, data
        )
    }

    fun postItem(siteID: UUID, avatar: Avatar, qwilightPostItem: EventOuterClass.Event.QwilightPostItem) {
        sites[siteID]?.postItem(
            avatar, qwilightPostItem
        )
    }

    fun getSiteID(siteID: String): UUID {
        return when (siteID) {
            "@Comment" -> commentSiteID
            "@Default" -> defaultSiteID
            "@Platform" -> platformSiteID
            else -> UUID.fromString(siteID)
        }
    }
}