let $MobEffectInstance = Java.loadClass("net.minecraft.world.effect.MobEffectInstance")
global.curiosEffectMap = {
    resist: [],
    persist: []
}

/**
 * 
 * @param {Internal.Player} player 
 */
global.getAllCurios = (player) => {
    let curios = player.nbt.ForgeCaps['curios:inventory']?.Curios
    if(curios == undefined) return undefined
    let all = []
    curios.forEach(slot => {
        slot.StacksHandler.Stacks.Items.forEach(item => all.push(item.id))
    })
    return all
}

/**
 * 
 * @param {Internal.CurioChangeEvent} event 
 */
 const curiosChange = event => {
    console.log("curios changed")
    const { entity:{server, uuid}} = event
    let player = server.playerList.getPlayer(uuid)

    player.persistentData.effectByCurios = player.persistentData.effectByCurios??[]
    player.persistentData.resistByCurios = player.persistentData.resistByCurios??[]
    let newEffectByCurios = new Set()
    let newResistByCurios = new Set()

    let allCurios = global.getAllCurios(player)
    if(allCurios === undefined) return;
    allCurios = new Set(allCurios)

    global.curiosEffectMap.persist.forEach(pair => {
        if(!allCurios.has(pair.curios)) return;
        pair.effectList.forEach(effect => {
            let effectInstance = new $MobEffectInstance(effect.effect, -1, effect.amplifier, Boolean(effect.ambient), Boolean(effect.visible))
            player.addEffect(effectInstance)
            effect.hash = effectInstance.hashCode()
            newEffectByCurios.add(effect)
        })
    })

    global.curiosEffectMap.resist.forEach(pair => {
        if(!allCurios.has(pair.curios)) return;
        pair.effectList.forEach(effect => {
            newResistByCurios.add(effect)
        })
    })

    player.persistentData.effectByCurios.forEach(effect=>{
        if(newEffectByCurios.has(effect)) return;
        player.server.scheduleInTicks(1, _ => {
            player.persistentData.curiosEffectToRemove = {
                tick: server.tickCount,
                hash: effect.hash || "ANY",
                effect: effect.effect || "ANY"
            }
            player.removeEffect(effect.effect||JSON.parse(effect))
        })
    })

    player.persistentData.effectByCurios = []
    player.persistentData.resistByCurios = []
    newEffectByCurios.forEach(e => player.persistentData.effectByCurios.push(e))
    newResistByCurios.forEach(e => player.persistentData.resistByCurios.push(e))
}

/**
 * 
 * @param {Internal.MobEffectEvent$Applicable} event 
 */
const effectResistance = event => {
    let player = Utils.server.playerList.getPlayer(event.entity.uuid)
    if(!player?.persistentData?.resistByCurios) return;

    const EffectRegex = new RegExp("effect\\.(.*)\\.(.*)")
    let effectInstance = event.effectInstance
    let effectId = EffectRegex.exec(effectInstance.descriptionId)
    if(!effectId) return;
    effectId = `${effectId[1]}:${effectId[2]}`
    let resistByCurios = JSON.parse(player.persistentData.resistByCurios)
    if(resistByCurios.find(effect => effect == effectId)){
        event.setResult(0)
    }
    if(!player.hasEffect(effectInstance.effect)) return;
    
    player.potionEffects.map.forEach((effect, instance) => {
        if(instance.effect != effectInstance.effect) return;
        if(!instance.isInfiniteDuration()) return;
        if(instance.amplifier <= effectInstance.amplifier) return;
        event.setResult(0)
    })
}

/**
 * 
 * @param {Internal.MobEffectEvent$Remove} event 
 */
const effectPersistence = event => {
    if(!event.effectInstance) return;
    let player = Utils.server.playerList.getPlayer(event.entity.uuid)
    if(!player?.persistentData?.effectByCurios) return;
    const EffectRegex = new RegExp("effect\\.(.*)\\.(.*)")
    let effectInstance = event.effectInstance
    let effectId = EffectRegex.exec(event.effectInstance.descriptionId)
    if(!effectId) return;
    effectId = `${effectId[1]}:${effectId[2]}`
    let effectHashCode = effectInstance.hashCode()

    let effectByCurios = player.persistentData.effectByCurios
    if(effectByCurios.indexOf(JSON.stringify(effectId)) != -1){
        event.setCanceled(true)
    }

    if(effectByCurios.find(effect => (effect.effect == effectId) && (effect.hash == effectHashCode))){
        event.setCanceled(true)
    }

    let effectToAddBack = effectByCurios.filter(effect => (effect.effect == effectId) && (effect.hash != effectHashCode))
    if(effectToAddBack.length){
        effectToAddBack.forEach(effect => {
            player.server.scheduleInTicks(1, _ => {
                player.potionEffects.add(effect.effect, -1, effect.amplifier, Boolean(effect.ambient), Boolean(effect.visible))
            })
        })
        return
    }
    let curiosEffectToRemove = player.persistentData.curiosEffectToRemove
    if(!curiosEffectToRemove) return;
    if(curiosEffectToRemove.tick != player.server.tickCount) return;
    if(curiosEffectToRemove.hash == "ANY"||curiosEffectToRemove.effect == "ANY") return;
    if(curiosEffectToRemove.hash == effectHashCode) return;
    if(curiosEffectToRemove.effect != effectId) return;

    let {effect, duration, amplifier, ambient, visible} = effectInstance
    let remainingEffect = new $MobEffectInstance(effect, duration, amplifier, ambient, visible)
    player.server.scheduleInTicks(1, _ => player.addEffect(remainingEffect))
    return
}

ForgeEvents.onEvent("top.theillusivec4.curios.api.event.CurioChangeEvent", event => {
    curiosChange(event)
})
ForgeEvents.onEvent("net.minecraftforge.event.entity.living.MobEffectEvent$Applicable", event => {
    effectResistance(event)
})
ForgeEvents.onEvent("net.minecraftforge.event.entity.living.MobEffectEvent$Remove", event => {
    effectPersistence(event)
})

