package pers.neige.neigeitems.papi

import com.alibaba.fastjson2.parseObject
import org.bukkit.Material
import org.bukkit.entity.Player
import pers.neige.neigeitems.NeigeItems
import pers.neige.neigeitems.action.ActionContext
import pers.neige.neigeitems.action.ContextKeys
import pers.neige.neigeitems.annotation.Awake
import pers.neige.neigeitems.hook.placeholderapi.PlaceholderExpansion
import pers.neige.neigeitems.manager.ActionManager
import pers.neige.neigeitems.manager.ConfigManager
import pers.neige.neigeitems.manager.HookerManager
import pers.neige.neigeitems.manager.HookerManager.papiHooker
import pers.neige.neigeitems.utils.ItemUtils.getItemId
import pers.neige.neigeitems.utils.ItemUtils.getNbt
import pers.neige.neigeitems.utils.ItemUtils.isNiItem
import java.io.InputStreamReader
import java.util.concurrent.ConcurrentHashMap
import javax.script.CompiledScript
import javax.script.SimpleBindings

/**
 * ni papi扩展
 */
object PapiExpansion {
    private var expansion: PlaceholderExpansion? = null
    private val engine = HookerManager.nashornHooker.getGlobalEngine().also { engine ->
        NeigeItems.getInstance().getResource("JavaScriptLib/lib.js")?.use { input ->
            InputStreamReader(input, "UTF-8").use { reader ->
                engine.eval(reader)
            }
        }
    }
    private val scripts = ConcurrentHashMap<String, CompiledScript>()

    @JvmStatic
    @Awake
    private fun init() {
        expansion = papiHooker?.newPlaceholderExpansion("ni", "Neige", "1.0.0") { player, param ->
            val params = param.split("_", limit = 2)
            when (val key = params[0].lowercase()) {
                "parse" -> {
                    return@newPlaceholderExpansion ActionManager.parseNode(
                        params.getOrNull(1) ?: return@newPlaceholderExpansion "", ActionContext.builder()
                            .caster(player)
                            .with(ContextKeys.PAPI_ENVIRONMENT, null)
                            .build()
                    )
                }

                "data", "nbt" -> {
                    // 玩家在线且当前PAPI变量输入了参数
                    if (player is Player && params.size == 2) {
                        // %ni_data/nbt_type_slot_content%
                        params[1].split("_", limit = 3).let {
                            if (it.size == 3) {
                                val type = it[0].lowercase()
                                val slot = it[1].lowercase()
                                val content = it[2]
                                val inventory = player.inventory
                                // 获取待检测物品
                                val itemStack = when (slot) {
                                    "hand" -> inventory.itemInMainHand
                                    "offhand" -> inventory.getItem(40)
                                    "head" -> inventory.getItem(39)
                                    "chest" -> inventory.getItem(38)
                                    "legs" -> inventory.getItem(37)
                                    "feet" -> inventory.getItem(36)
                                    else -> slot.toIntOrNull()?.let { index -> inventory.getItem(index) }
                                } ?: return@newPlaceholderExpansion ""
                                if (itemStack.type == Material.AIR) return@newPlaceholderExpansion ""
                                if (key == "data") {
                                    val itemInfo = itemStack.isNiItem(true) ?: return@newPlaceholderExpansion ""
                                    val data: HashMap<String, String> = itemInfo.data
                                    when (type) {
                                        "get" -> return@newPlaceholderExpansion data[content] ?: ""
                                        "has" -> return@newPlaceholderExpansion data.containsKey(content).toString()
                                        "check" -> {
                                            content.parseObject<java.util.HashMap<String, String?>>()
                                                .forEach { (key, value) ->
                                                    if (value == null) {
                                                        if (data.containsKey(key)) return@newPlaceholderExpansion "false"
                                                    } else {
                                                        if (data[key] != value) return@newPlaceholderExpansion "false"
                                                    }
                                                }
                                            return@newPlaceholderExpansion "true"
                                        }

                                        "eval" -> {
                                            if (!ConfigManager.enableJsPapi) return@newPlaceholderExpansion ""
                                            val bindings = SimpleBindings()
                                            bindings["player"] = player
                                            bindings["itemStack"] = itemStack
                                            bindings["id"] = itemInfo.id
                                            bindings["itemTag"] = itemInfo.itemTag
                                            bindings["data"] = data
                                            return@newPlaceholderExpansion scripts.computeIfAbsent(content) {
                                                HookerManager.nashornHooker.compile(engine, content)
                                            }.eval(bindings)?.toString() ?: ""
                                        }
                                    }
                                } else {
                                    val itemTag = itemStack.getNbt()
                                    when (type) {
                                        "get" -> return@newPlaceholderExpansion itemTag.getDeepStringOrNull(content)
                                            ?: ""

                                        "has" -> return@newPlaceholderExpansion (itemTag.getDeep(content) != null).toString()
                                        "check" -> {
                                            content.parseObject<java.util.HashMap<String, String?>>()
                                                .forEach { (key, value) ->
                                                    if (value == null) {
                                                        if (itemTag.getDeep(key) == null) return@newPlaceholderExpansion "false"
                                                    } else {
                                                        if (itemTag.getDeepStringOrNull(key) != value) return@newPlaceholderExpansion "false"
                                                    }
                                                }
                                            return@newPlaceholderExpansion "true"
                                        }

                                        "eval" -> {
                                            if (!ConfigManager.enableJsPapi) return@newPlaceholderExpansion ""
                                            val bindings = SimpleBindings()
                                            bindings["player"] = player
                                            bindings["itemStack"] = itemStack
                                            bindings["itemTag"] = itemTag
                                            return@newPlaceholderExpansion scripts.computeIfAbsent(content) {
                                                HookerManager.nashornHooker.compile(engine, content)
                                            }.eval(bindings)?.toString() ?: ""
                                        }
                                    }
                                }
                            }
                        }
                    }
                    return@newPlaceholderExpansion ""
                }

                "amount" -> {
                    if (player !is Player) return@newPlaceholderExpansion "0"
                    val itemId = params.getOrNull(1) ?: return@newPlaceholderExpansion "0"
                    var result = 0
                    val contents = player.inventory.contents
                    for (itemStack in contents) {
                        val currentItemId = itemStack.getItemId()
                        if (currentItemId == itemId) {
                            result += itemStack.amount
                        }
                    }
                    return@newPlaceholderExpansion result.toString()
                }

                "count" -> {
                    if (player !is Player) return@newPlaceholderExpansion "false"
                    val argsString = params.getOrNull(1) ?: return@newPlaceholderExpansion "false"
                    val args = argsString.split("_", "\\")
                    val items = HashMap<String, Int>()
                    var isId = true
                    var id: String? = null
                    for (arg in args) {
                        if (isId) {
                            id = arg
                        } else {
                            val amount = arg.toIntOrNull()
                            if (amount != null && amount > 0 && id != null) {
                                items[id] = amount
                            }
                        }
                        isId = !isId
                    }
                    if (items.isEmpty()) return@newPlaceholderExpansion "true"
                    val contents = player.inventory.contents
                    for (itemStack in contents) {
                        val currentItemId = itemStack.getItemId() ?: continue
                        val pre = items[currentItemId] ?: continue
                        if (pre > itemStack.amount) {
                            items[currentItemId] = pre - itemStack.amount
                        } else {
                            items.remove(currentItemId)
                        }
                    }
                    return@newPlaceholderExpansion items.isEmpty().toString()
                }

                else -> return@newPlaceholderExpansion ""
            }
        }
        expansion?.register()
    }
}