package at.hannibal2.skyhanni.data

import at.hannibal2.skyhanni.events.InventoryOpenEvent
import at.hannibal2.skyhanni.events.LorenzActionBarEvent
import at.hannibal2.skyhanni.events.ProfileApiDataLoadedEvent
import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NumberUtil.romanToDecimal
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.regex.Pattern

class SkillExperience {

    private val actionBarPattern = Pattern.compile("(?:.*)§3\\+(?:.*) (.*) \\((.*)\\/(.*)\\)(?:.*)")
    private val inventoryPattern = Pattern.compile("(?:.*) §e(.*)§6\\/(?:.*)")

    @SubscribeEvent
    fun onProfileDataLoad(event: ProfileApiDataLoadedEvent) {
        val profileData = event.profileData
        for ((key, value) in profileData.entrySet()) {
            if (key.startsWith("experience_skill_")) {
                val label = key.substring(17)
                val exp = value.asLong
                skillExp[label] = exp
            }
        }
    }

    @SubscribeEvent
    fun onProfileJoin(event: ProfileJoinEvent) {
        skillExp.clear()
    }

    @SubscribeEvent
    fun onActionBar(event: LorenzActionBarEvent) {
        if (!LorenzUtils.inSkyBlock) return

        val matcher = actionBarPattern.matcher(event.message)
        if (!matcher.matches()) return

        val skill = matcher.group(1).lowercase()
        val overflow = matcher.group(2).formatNumber()
        val neededForNextLevel = matcher.group(3).formatNumber()
        val nextLevel = getLevelForExpExactly(neededForNextLevel)
        val baseExp = getExpForLevel(nextLevel - 1)
        skillExp[skill] = baseExp + overflow
    }

    @SubscribeEvent
    fun onInventoryOpen(event: InventoryOpenEvent) {
        if (event.inventoryName != "Your Skills") return

        for ((_, stack) in event.inventoryItems) {
            val name = stack.name?.removeColor() ?: continue
            if (!name.contains(" ")) continue

            val lore = stack.getLore()

            var next = false
            for (line in lore) {
                if (line.contains("Progress to Level")) {
                    next = true
                    continue
                }
                if (next) {
                    val split = name.split(" ")
                    val skillName = split[0].lowercase()
                    val level = split[1].romanToDecimal()
                    val baseExp = getExpForLevel(level)
                    val matcher = inventoryPattern.matcher(line)
                    if (matcher.matches()) {
                        val rawNumber = matcher.group(1)
                        val overflow = rawNumber.formatNumber()
                        val experience = baseExp + overflow
                        skillExp[skillName] = experience
                        println("skill exp: $skillName -> $experience")
                    }
                    next = false
                }
            }
        }
        if (skillExp.isNotEmpty()) return
    }

    companion object {
        private val skillExp = mutableMapOf<String, Long>()

        private fun getLevelForExpExactly(experience: Long): Int {
            var level = 1
            for (levelXp in levelingExp) {
                if (levelXp.toLong() == experience) {
                    return level
                }
                level++
            }

            return 0
        }

        fun getExpForLevel(requestedLevel: Int): Long {
            var total = 0L
            var level = 0
            for (levelXp in levelingExp) {
                total += levelXp
                level++
                if (level == requestedLevel) {
                    return total
                }
            }

            return 0
        }

        //TODO create additional event
        fun getExpForSkill(skillName: String) = skillExp[skillName.lowercase()] ?: 0

        private val levelingExp = listOf(
            50,
            125,
            200,
            300,
            500,
            750,
            1000,
            1500,
            2000,
            3500,
            5000,
            7500,
            10000,
            15000,
            20000,
            30000,
            50000,
            75000,
            100000,
            200000,
            300000,
            400000,
            500000,
            600000,
            700000,
            800000,
            900000,
            1000000,
            1100000,
            1200000,
            1300000,
            1400000,
            1500000,
            1600000,
            1700000,
            1800000,
            1900000,
            2000000,
            2100000,
            2200000,
            2300000,
            2400000,
            2500000,
            2600000,
            2750000,
            2900000,
            3100000,
            3400000,
            3700000,
            4000000,
            4300000,
            4600000,
            4900000,
            5200000,
            5500000,
            5800000,
            6100000,
            6400000,
            6700000,
            7000000
        )
    }
}

private fun String.formatNumber(): Long {
    var text = replace(",", "")
    val multiplier = if (text.endsWith("k")) {
        text = text.substring(0, text.length - 1)
        1_000
    } else if (text.endsWith("m")) {
        text = text.substring(0, text.length - 1)
        1_000_000
    } else 1
    val d = text.toDouble()
    return (d * multiplier).toLong()
}