import { Player, EntityDamageCause, system, Entity, Block, Dimension, GameMode, ItemStack, EquipmentSlot } from '@minecraft/server'
import { multiClicking, testing } from '../../utils/systemHelper'
import { DUMMY } from '../../entities/specific/dummy'
import { EntityTypeIds } from '../../entities/utils/entityTypesIds'
import { EntityProperites } from '../../entities/utils/entityProperties'
import { EntityHelper } from '../../entities/utils/entityHelper'
import { EntityComponents } from '../../entities/utils/entityComponents'
import { BlockHelper } from '../utils/blockHelpers'
import { ItemHelper } from '../../items/utils/itemHelper'
import { SoundsIds } from '../../utils/typingConstants'
import { BlockStateNames } from '../utils/blockConstants'

export const CACTUS_SPIKE_TRAPS = {
    _normalBlockTag: "piki_beyondsurvival:spike_trap",
    _poisonousBlockTag: "piki_beyondsurvival:spike_trap_poisonous",
    _cactusSpikeRepair: "piki_beyondsurvival:cactus_spike",
    _poisonContactEffect: EntityHelper.createEntityEffect("poison", 6, 0, true),
    _onStepDamage: 5,
    _onStepDamageCause: EntityDamageCause.contact,
    _tickDelay: 20 * 0.5,
    _dummyDynamicPropertyType: "cactus_spike_trap",
    _tickDistance: 10,
    _damagesFamily: ["mob", "player"],
    _killLimit: 16,
    _playSoundKill: 4,
    _repairTimes: 3,


    isRepairingTrap(itemTypeId, block, source) {
        const isRepairing = this.isCactusSpikeTrap(block) && itemTypeId == this._cactusSpikeRepair
        if (!isRepairing) return false
        if (multiClicking(source)) return false
        return true
    },

    /**
     * 
     * @param {Block|Entity} value 
     * @returns 
     */
    isCactusSpikeTrap(value) {
        try {
            if (value instanceof Block) {
                return value.hasTag(this._normalBlockTag) || value.hasTag(this._poisonousBlockTag)
            }
            else if (value instanceof Entity) {
                return DUMMY.getType(value) == this._dummyDynamicPropertyType
            }
        } catch {
            return false
        }
    },

    /**
     * 
     * @param {Block} block 
     * @param {ItemStack} itemStack 
     * @param {Entity} source
     */
    repairTrap(block, itemStack, source) {
        const {x, y, z} = block.location
        const dummy = BlockHelper.getDummy(block)
        
        if (!dummy) return // testing.error("Cactus Spike doesn't have a dummy")

        const killed = dummy.getDynamicProperty(EntityProperites.cactusKilledEntities) ?? 0
        let spikesReceived = dummy.getDynamicProperty(EntityProperites.cactusSpikesReceived) ?? 0
        
        if (killed < this._killLimit) return // testing.error("Cactus Spike is not broken")

        if (itemStack.amount >= this._repairTimes) {
            ItemHelper.clearPlayerItem(source, EquipmentSlot.Mainhand, this._repairTimes)
            dummy.setDynamicProperty(EntityProperites.cactusSpikesReceived, 0)
            dummy.setDynamicProperty(EntityProperites.cactusKilledEntities, 0)
            system.run(() => {
                const fixedState = block.permutation.withState(BlockStateNames.customIsBroken, false)
                block.setPermutation(fixedState)
            })
        } else {
            ItemHelper.clearPlayerItem(source, EquipmentSlot.Mainhand)
            spikesReceived++
            dummy.setDynamicProperty(EntityProperites.cactusSpikesReceived, spikesReceived)
            if (spikesReceived < this._repairTimes) return
            dummy.setDynamicProperty(EntityProperites.cactusSpikesReceived, 0)
            dummy.setDynamicProperty(EntityProperites.cactusKilledEntities, 0)
            system.run(() => {
                const fixedState = block.permutation.withState(BlockStateNames.customIsBroken, false)
                block.setPermutation(fixedState)
            })
        }
    },

    /**
     * 
     * @param {Player} player 
     */
    steppedDamage(player) {
		const cactusSpikeTrapEntities = player.dimension.getEntities({
            location: player.location, 
            maxDistance: this._tickDistance, 
            type: EntityTypeIds.dummy
        })
        const {effectType, duration, amplifier, showParticles} = this._poisonContactEffect

		cactusSpikeTrapEntities.forEach(blockEntity => {
            const isInCooldown = blockEntity.getDynamicProperty(EntityProperites.isCactusSpikeCooldown)

            let killed = blockEntity.getDynamicProperty(EntityProperites.cactusKilledEntities) ?? 0

            // Do not kill entity, it just means it's a different type or cooling down
            if (!this.isCactusSpikeTrap(blockEntity)) return 

			const location = blockEntity.location;
			const block = player.dimension.getBlock(location)

            // Do not kill the entity, it just means the block might be unloaded.
            if (!block) return
            // Kill the entity, it's in the wrong location
			if (!this.isCactusSpikeTrap(block)) return DUMMY.kill(blockEntity, "Block isn't Cactus Spike Trap")
            
            if (!blockEntity.isValid()) return;

            const isBroken = block.permutation.getState(BlockStateNames.customIsBroken)
            
            if (isBroken) return
            if (isInCooldown) return
            if (killed >= this._killLimit) {
                // testing.error("Cactus is broken")
                system.run(() => {
                    const brokenState = block.permutation.withState(BlockStateNames.customIsBroken, true)
                    block.setPermutation(brokenState)
                })
                return 
            }
            blockEntity.setDynamicProperty(EntityProperites.isCactusSpikeCooldown, true)

            const targetEntities = blockEntity.dimension.getEntitiesAtBlockLocation(location)
            
            for (const entity of targetEntities) {
                if (!this._damagesFamily.some(f => EntityComponents.getFamilyType(entity)?.hasTypeFamily(f))) continue
                if (!EntityHelper.entityHasVelocity(entity, 0.0001)) continue
                if (killed >= this._killLimit) return // testing.error("Cactus is broken")

                entity.applyDamage(this._onStepDamage, {cause: this._onStepDamageCause})

                const health = EntityComponents.getHealth(entity).currentValue
                if (health <= 0) {
                    killed++
                    blockEntity.setDynamicProperty(EntityProperites.cactusKilledEntities, killed)
                    if (killed % this._playSoundKill == 0) entity.dimension.playSound(SoundsIds.mobZombieWoodbreak, blockEntity.location)
                } else {
                    // Apply Poison
                    if (!block.hasTag(this._poisonousBlockTag)) continue
                    if (entity instanceof Player && entity.getGameMode() == GameMode.creative) continue
                    entity.addEffect(effectType, duration, {amplifier: amplifier, showParticles: showParticles})
                }
            }

                
            // Remove the cooldown indicateor a second before the next tick delay
            system.runTimeout(() => {
                if (!blockEntity.isValid()) return
                blockEntity.setDynamicProperty(EntityProperites.isCactusSpikeCooldown, false)
            }, this._tickDelay - 1)       
		})
    },

    /**
     * @param {Block} block
     * @returns 
     */
    wasPlaced(block) {
        const location = block.location
        // testing.info("Placed cactus spike trap at", location.x, location.y, location.z)
        system.run(() => {
            const {dimension, location} = block 
            DUMMY.summon(dimension, location, this._dummyDynamicPropertyType)
        })
    },

    /**
     * 
     * @param {Dimension} dimension 
     * @param {import('@minecraft/server').Vector3} location 
     */
    wasDestroyed(dimension, location) {
        // testing.info("Destroyed cactus spike trap at", location.x, location.y, location.z)
        const cactusEntity = dimension.getEntities({
            location: location, 
            type: EntityTypeIds.dummy,
        })?.[0]

	    if (cactusEntity && this.isCactusSpikeTrap(cactusEntity)) DUMMY.kill(cactusEntity, "cactus spike trap destroyed");
    },

    /**
     * 
     * @returns {boolean}
     */
    shouldRunDamage() {
        return system.currentTick % this._tickDelay == 0
    }
}