import { system, world, Player, MolangVariableMap, EntityDamageCause, InputPermissionCategory } from "@minecraft/server";
import * as attackUtils from "../attackUtils.js";

export const staticEffectMethods = {
    hasStatic: false,
    lastEffectTick: 0,
    staticUpdateTickInterval: 4,
    staticEffectsUpdateTickInterval: 10,
    staticDecayTime: 100,
    staticTickRunId: null,
    staticMolang: new MolangVariableMap(),
    staticTick() {
        if (!this.isValid() || !this.hasStatic) return;

        try {
            let currentStatic = this.getDynamicProperty("oct_eg:static_amount") ?? 0;
            const lastUpdatedTime = this.getDynamicProperty("oct_eg:static_updated_time") ?? system.currentTick;
            const ticksElapsed = system.currentTick - lastUpdatedTime;

            // Static decay
            if (ticksElapsed >= this.staticDecayTime) {
                currentStatic = Math.max(0, currentStatic - 1);
                this.setDynamicProperty("oct_eg:static_amount", currentStatic);
                this.setDynamicProperty("oct_eg:static_updated_time", system.currentTick);
            }
            // Stop if no static left
            if (currentStatic <= 0) {
                system.clearRun(this.staticTickRunId);
                this.setDynamicProperty("oct_eg:static_adds_weakness", false);
                this.staticTickRunId = null;
                this.hasStatic = false;
                this.lastEffectTick = 0;
                return;
            }

            if (system.currentTick - this.lastEffectTick >= this.staticEffectsUpdateTickInterval) {
                this.staticEffects(currentStatic);
            }

        } catch (e) {
            //console.error(`§cError in staticTick: ${e}`);
        }
    },
    staticEffects(currentStatic) {
        if (!this.isValid()) return;
        //world.sendMessage(`§dstaticEffects`);

        try {
            const headLoc = this.getHeadLocation();
            const shouldAddWeakness = this.getDynamicProperty("oct_eg:static_adds_weakness") ?? 0;

            this.staticMolang.setFloat('num', currentStatic);
            this.dimension.spawnParticle("oct_eg:static_number", headLoc, this.staticMolang);

            if (shouldAddWeakness && currentStatic >= 3) {
                this.addEffect("minecraft:weakness", 20, {
                    amplifier: 1,
                    showParticles: true
                });
            }

            this.lastEffectTick = system.currentTick;

        } catch (e) {
            //console.error(`§cError in staticEffects: ${e}`);
        }
    },
    addStatic(amount = 1, maxStatic = 3) {
        if (!this.isValid()) return;

        try {
            const currentStatic = this.getDynamicProperty("oct_eg:static_amount") ?? 0;
            const newStatic = Math.min(currentStatic + amount, maxStatic);

            this.setDynamicProperty("oct_eg:static_amount", newStatic);
            this.setDynamicProperty("oct_eg:static_updated_time", system.currentTick);

            this.hasStatic = true;
            this.staticEffects(newStatic);

            if (!this.staticTickRunId) {
                this.staticTickRunId = system.runInterval(() => this.staticTick(), this.staticUpdateTickInterval);
            }

        } catch (e) {
            //console.error(`§cError in addStatic: ${e}`);
        }
    },
    removeStatic(amount = 1) {
        if (!this.isValid()) return 0;

        try {
            const currentStatic = this.getDynamicProperty("oct_eg:static_amount") ?? 0;
            const newStatic = Math.max(currentStatic - amount, 0);

            this.setDynamicProperty("oct_eg:static_amount", newStatic);
            this.setDynamicProperty("oct_eg:static_updated_time", system.currentTick);

            return currentStatic - newStatic;

        } catch (e) {
            //console.error(`§cError in removeStatic: ${e}`);
            return 0;
        }
    },
    staticEffectOnLoad() {
        if (!this.isValid()) return;
        try {
            const currentStatic = this.getDynamicProperty("oct_eg:static_amount") ?? 0;

            if (currentStatic > 0 && (!this.hasStatic || !this.staticTickRunId)) {
                this.hasStatic = true;
                this.staticTickRunId = system.runInterval(() => this.staticTick(), this.staticUpdateTickInterval);
            }

        } catch (e) {
            //console.error(`§cError in staticEffectOnLoad: ${e}`);
        }
    },
    getStatic() {
        return this.getDynamicProperty("oct_eg:static_amount") ?? 0;
    }
}

export const sparksEffectMethods = {
    hasSparks: false,
    lastEffectTick: 0,
    sparksUpdateTickInterval: 4,
    sparksEffectTickInterval: 20,
    sparksTickRunId: null,
    sparksMolang: new MolangVariableMap(),
    defaultSparkDamage: 2,
    sparkDamage: 2,
    sparkDamageRange: 4,
    playerCaster: undefined,
    sparksTick() {
        if (!this.isValid() || !this.hasSparks) return;
        try {
            let currentTimeLeft = this.getDynamicProperty("oct_eg:sparks_time_left") ?? 0;
            const lastUpdatedTime = this.getDynamicProperty("oct_eg:sparks_updated_time") ?? system.currentTick;
            const ticksElapsed = system.currentTick - lastUpdatedTime;

            // Effect Time update
            if (ticksElapsed >= this.sparksUpdateTickInterval) {
                currentTimeLeft = Math.max(0, currentTimeLeft - ticksElapsed);

                this.setDynamicProperty("oct_eg:sparks_time_left", currentTimeLeft);
                this.setDynamicProperty("oct_eg:sparks_updated_time", system.currentTick);
            }

            if (currentTimeLeft <= 0) { // Effect ended
                system.clearRun(this.sparksTickRunId);
                this.sparksTickRunId = null;
                this.hasSparks = false;
                this.lastEffectTick = 0;
                this.sparkDamage = this.defaultSparkDamage;
                this.playerCaster = undefined;
                return;
            }

            // Sparks effect
            if (system.currentTick - this.lastEffectTick >= this.sparksEffectTickInterval) {
                this.lastEffectTick = system.currentTick;
                this.sparksEffects();
            }

            // Effect active particle
            this.dimension.spawnParticle("oct_eg:sparks_effect", this.location);

        } catch (e) {
            //console.error(`§cError in sparksTick: ${e}`);
        }
    },
    sparksEffects() {
        if (!this.isValid()) return;
        //world.sendMessage(`sparksEffects`);
        try {

            for (const entity of attackUtils.getValidTargetsInRange(
                this,
                {
                    maxDistance: this.sparkDamageRange,
                    excludedEntities: [this.playerCaster]
                }
            )) {
                attackUtils.applyModifiedDamage(entity, this.sparkDamage, {
                    cause: EntityDamageCause.lightning,
                    damagingEntity: this.playerCaster
                });

                const bounceStartLocation = this.getHeadLocation();
                const bounceEndLocation = entity.getHeadLocation();
                this.sparksMolang.setFloat("offset_x", bounceEndLocation.x - bounceStartLocation.x);
                this.sparksMolang.setFloat("offset_y", bounceEndLocation.y - bounceStartLocation.y + Math.random() - 0.5);
                this.sparksMolang.setFloat("offset_z", bounceEndLocation.z - bounceStartLocation.z);

                entity.dimension.spawnParticle("oct_eg:thunder_punch.bounce", bounceStartLocation, this.sparksMolang);
                entity.dimension.playSound("octovon.eg.gauntlet.electro.thunder_punch.zap", entity.location);
            }

        } catch (e) {
            //console.error(`§cError in sparksEffects: ${e}`);
        }
    },
    addSparks(amount = 20, sparkDamage = this.defaultSparkDamage, caster = undefined) {
        if (!this.isValid()) return;

        try {
            const currentTime = this.getDynamicProperty("oct_eg:sparks_time_left") ?? 0;
            const newTime = currentTime + amount;

            this.setDynamicProperty("oct_eg:sparks_time_left", newTime);
            this.setDynamicProperty("oct_eg:sparks_updated_time", system.currentTick);
            this.sparkDamage = sparkDamage;
            this.playerCaster = caster;

            if (!this.hasSparks || !this.sparksTickRunId) {
                this.hasSparks = true;
                this.sparksTickRunId = system.runInterval(() => this.sparksTick(), this.sparksUpdateTickInterval);
            }

        } catch (e) {
            //console.error(`§cError in addSparks: ${e}`);
        }
    },
    sparksEffectOnLoad() {
        if (!this.isValid()) return;
        try {
            //console.log(`§2Sparks: Loaded`);
            const currentTimeLeft = this.getDynamicProperty("oct_eg:sparks_time_left") ?? 0;

            if (currentTimeLeft > 0 && (!this.sparksTickRunId || !this.hasSparks)) {
                this.hasSparks = true;
                this.sparksTickRunId = system.runInterval(() => this.sparksTick(), this.sparksUpdateTickInterval);
                // console.log(`§2Sparks: Started Again`);
            }

        } catch (e) {
            //console.error(`§cError in sparksEffectOnLoad: ${e}`);
        }
    }
}

export const stunEffectMethods = {
    hasStun: false,
    stunUpdateTickInterval: 4,
    stunEffectTickInterval: 20,
    stunTickRunId: null,
    lastStunEffectTick: 0,

    stunTick() {
        if (!this.isValid() || !this.hasStun) return;

        try {

            if (this.gauntletManager !== undefined) {
                this.gauntletManager.cancelActiveAttack();
            }

            let stunTimeLeft = this.getDynamicProperty("oct_eg:stun_time_left") ?? 0;
            const lastUpdatedTime = this.getDynamicProperty("oct_eg:stun_updated_time") ?? system.currentTick;
            const ticksElapsed = system.currentTick - lastUpdatedTime;

            // Update time left
            if (ticksElapsed >= this.stunUpdateTickInterval) {
                stunTimeLeft = Math.max(0, stunTimeLeft - ticksElapsed);
                this.setDynamicProperty("oct_eg:stun_time_left", stunTimeLeft);
                this.setDynamicProperty("oct_eg:stun_updated_time", system.currentTick);
            }

            if (stunTimeLeft <= 0) {
                system.clearRun(this.stunTickRunId);

                if (this instanceof Player) this.stunnedPlayerReset();

                this.stunTickRunId = null;
                this.hasStun = false;
                this.lastStunEffectTick = 0;
                return;
            }

            // Stun effect
            if (system.currentTick - this.lastStunEffectTick >= this.stunEffectTickInterval) {
                this.lastStunEffectTick = system.currentTick;
                this.applyStunEffect();
            }

        } catch (e) {
            //console.error(`§cError in stunTick: ${e}`);
        }
    },

    applyStunEffect() {
        if (!this.isValid()) return;

        try {
            const isPlayer = this instanceof Player;
            if (isPlayer) {
                this.stunnedPlayer();
            } else {
                this.stunnedEntity();
            }
            this.dimension.spawnParticle("oct_eg:stun", this.getHeadLocation());

        } catch (e) {
            //console.error(`§cError in applyStunEffect: ${e}`);
        }
    },

    addStun(duration = 20) {
        if (!this.isValid()) return;

        try {
            const currentTime = this.getDynamicProperty("oct_eg:stun_time_left") ?? 0;

            // Only update if the new duration is longer
            if (duration > currentTime) {
                this.setDynamicProperty("oct_eg:stun_time_left", duration);
                this.setDynamicProperty("oct_eg:stun_updated_time", system.currentTick);
            }

            if (!this.hasStun || !this.stunTickRunId) {
                this.hasStun = true;
                this.stunTickRunId = system.runInterval(() => this.stunTick(), this.stunUpdateTickInterval);
            }

        } catch (e) {
            //console.error(`§cError in addStun: ${e}`);
        }
    },

    stunEffectOnLoad() {
        if (!this.isValid()) return;

        try {
            const timeLeft = this.getDynamicProperty("oct_eg:stun_time_left") ?? 0;

            if (timeLeft > 0 && (!this.stunTickRunId || !this.hasStun)) {
                this.hasStun = true;
                this.stunTickRunId = system.runInterval(() => this.stunTick(), this.stunUpdateTickInterval);
            }

        } catch (e) {
            //console.error(`§cError in stunEffectOnLoad: ${e}`);
        }
    },

    stunnedPlayer() {
        this.inputPermissions.setPermissionCategory(InputPermissionCategory.Movement, false);
        this.addEffect("minecraft:slowness", 22, {
            amplifier: 1,
            showParticles: false
        });
        this.addEffect("minecraft:weakness", 22, {
            amplifier: 254,
            showParticles: false
        });
    },

    stunnedPlayerReset() {
        this.inputPermissions.setPermissionCategory(InputPermissionCategory.Movement, true);
    },

    stunnedEntity() {
        this.addEffect("minecraft:slowness", 22, {
            amplifier: 254,
            showParticles: false
        });
        this.addEffect("minecraft:weakness", 22, {
            amplifier: 254,
            showParticles: false
        });
    }

};