import {
    Container,
    EntityComponentTypes,
    EntityDamageCause,
    EntityEquippableComponent,
    EntityProjectileComponent,
    EquipmentSlot,
    ItemComponentTypes,
    ItemDurabilityComponent,
    ItemEnchantableComponent,
    ItemStack,
    Player,
    system,
    world,
} from "@minecraft/server";
import { replaceableHotbarItems, replaceableItems } from "../data/items";
import { Clowns, Entities } from "../entities/Entities";
import {
    getHotbarItems,
    getInventoryItems,
    getRandomBetween,
    isPlayerLogged,
    replaceHotbarItems,
    replaceInventoryItems,
} from "../utils/utils";

world.beforeEvents.worldInitialize.subscribe((event) => {
    const equippable = event.itemComponentRegistry.registerCustomComponent("px_hc:equippable", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            /**
             * @type {EntityEquippableComponent}
             */
            const sourceEquippableComponent = source.getComponent(EntityComponentTypes.Equippable);

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    source.playSound("armor.equip_generic", { volume: 1.0 });

                    /**
                     * @type {ItemDurabilityComponent}
                     */
                    const durability = itemStack.getComponent(ItemComponentTypes.Durability);
                    /**
                     * @type {ItemEnchantableComponent}
                     */
                    const enchantable = itemStack.getComponent(ItemComponentTypes.Enchantable);

                    if (durability && enchantable) {
                        if (sourceEquippableComponent) {
                            const equipmentSlot = replaceableItems.get(itemStack.typeId);

                            if (equipmentSlot) {
                                const item = new ItemStack(equipmentSlot.item);

                                /**
                                 * @type {ItemDurabilityComponent}
                                 */
                                const replacedItemDurability = item.getComponent(
                                    ItemComponentTypes.Durability
                                );
                                /**
                                 * @type {ItemEnchantableComponent}
                                 */
                                const replacedItemEnchantable = item.getComponent(
                                    ItemComponentTypes.Enchantable
                                );

                                if (replacedItemDurability && replacedItemEnchantable) {
                                    replacedItemDurability.damage = durability.damage;

                                    replacedItemEnchantable.addEnchantments(
                                        enchantable.getEnchantments()
                                    );

                                    const hasEquippedItem = sourceEquippableComponent.getEquipment(
                                        equipmentSlot.slot
                                    );

                                    if (hasEquippedItem) {
                                        sourceEquippableComponent.setEquipment(
                                            EquipmentSlot.Mainhand,
                                            sourceEquippableComponent.getEquipment(
                                                equipmentSlot.slot
                                            )
                                        );

                                        sourceEquippableComponent.setEquipment(
                                            equipmentSlot.slot,
                                            item
                                        );
                                    } else {
                                        sourceEquippableComponent.setEquipment(
                                            EquipmentSlot.Mainhand
                                        );

                                        sourceEquippableComponent.setEquipment(
                                            equipmentSlot.slot,
                                            item
                                        );
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
    });
    const knockup = event.itemComponentRegistry.registerCustomComponent("px_hc:knockup", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const nearbyEntities = source.dimension.getEntities({
                location: source.location,
                excludeTypes: ["minecraft:item", "minecraft:xp_orb"],
                maxDistance: 8,
            });

            const knockupVerticalPower = 1.2;

            const damage = 8;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    source.playSound("strongman.attack_2", { volume: 1.0 });

                    system.runTimeout(() => {
                        if (isPlayerLogged(source)) {
                            source.dimension.spawnParticle("px_hc:hammer_impact", {
                                x: source.location.x,
                                y: source.location.y,
                                z: source.location.z,
                            });

                            for (let entity of nearbyEntities) {
                                if (!(entity instanceof Player)) {
                                    entity.applyDamage(damage, {
                                        cause: EntityDamageCause.entityAttack,
                                        damagingEntity: source,
                                    });
                                    entity.applyImpulse({ x: 0, y: knockupVerticalPower, z: 0 });
                                }
                            }
                        }
                    }, 0.5 * 20);
                }
            }
        },
    });
    const knockup_hammer = event.itemComponentRegistry.registerCustomComponent(
        "px_hc:knockup_hammer",
        {
            onUse: (iEvent) => {
                const { source, itemStack } = iEvent;

                const nearbyEntities = source.dimension.getEntities({
                    location: source.location,
                    excludeTypes: ["minecraft:item", "minecraft:xp_orb"],
                    maxDistance: 8,
                });

                const knockupVerticalPower = 1.2;

                const damage = 8;

                const cooldown = itemStack.getComponent("minecraft:cooldown");

                if (cooldown) {
                    const cooldownTicks = cooldown?.cooldownTicks;
                    const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                    if (remainingCooldown == cooldownTicks - 1) {
                        cooldown.startCooldown(source);

                        source.playSound("harley_quinn.funny_hammer", { volume: 0.2 });

                        system.runTimeout(() => {
                            if (isPlayerLogged(source)) {
                                source.dimension.spawnParticle("px_hc:hammer_impact", {
                                    x: source.location.x,
                                    y: source.location.y,
                                    z: source.location.z,
                                });

                                for (let entity of nearbyEntities) {
                                    if (!(entity instanceof Player)) {
                                        entity.applyDamage(damage, {
                                            cause: EntityDamageCause.entityAttack,
                                            damagingEntity: source,
                                        });
                                        entity.applyImpulse({
                                            x: 0,
                                            y: knockupVerticalPower,
                                            z: 0,
                                        });
                                    }
                                }
                            }
                        }, 0.6 * 20);
                    }
                }
            },
        }
    );
    const screamAttack = event.itemComponentRegistry.registerCustomComponent("px_hc:scream", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const nearbyEntities = source.dimension.getEntities({
                location: source.location,
                excludeTypes: ["minecraft:item", "minecraft:xp_orb"],
                maxDistance: 8,
            });

            const damage = 8;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    source.playSound("stilt_walker.attack_2", { volume: 0.2 });

                    system.runTimeout(() => {
                        if (isPlayerLogged(source)) {
                            screamParticle(source);

                            for (let entity of nearbyEntities) {
                                if (!(entity instanceof Player)) {
                                    entity.applyDamage(damage, {
                                        cause: EntityDamageCause.entityAttack,
                                        damagingEntity: source,
                                    });
                                }
                            }
                        }
                    }, 0.1 * 20);
                }
            }
        },
    });
    const summonBunnies = event.itemComponentRegistry.registerCustomComponent(
        "px_hc:summon_bunnies",
        {
            onUse: (iEvent) => {
                const { source, itemStack } = iEvent;

                const entityCount = 3;

                const cooldown = itemStack.getComponent("minecraft:cooldown");

                if (cooldown) {
                    const cooldownTicks = cooldown?.cooldownTicks;
                    const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                    if (remainingCooldown == cooldownTicks - 1) {
                        cooldown.startCooldown(source);

                        source.playSound("illusionist.bunny", { volume: 1.0 });

                        system.runTimeout(() => {
                            if (isPlayerLogged(source)) {
                                for (let e = 0; e < entityCount; e++) {
                                    const bunny = source.dimension.spawnEntity(
                                        Entities.bunny_monster,
                                        {
                                            x: source.location.x + getRandomBetween(-3, 3),
                                            y: source.location.y,
                                            z: source.location.z + getRandomBetween(-3, 3),
                                        }
                                    );

                                    bunny.triggerEvent("bunny:tameable");

                                    bunny.nameTag = "Bunny Monster";

                                    system.runTimeout(() => {
                                        const tameable = bunny.getComponent(
                                            EntityComponentTypes.Tameable
                                        );

                                        if (tameable) {
                                            tameable.tame(source);
                                        }
                                    }, 0.1 * 20);
                                }
                            }
                        }, 4 * 20);
                    }
                }
            },
        }
    );
    const chainsaw = event.itemComponentRegistry.registerCustomComponent("px_hc:chainsaw", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            /**
             * @type {Container}
             */
            const inventory = source.getComponent(EntityComponentTypes.Inventory)?.container;

            /**
             * @type {EntityEquippableComponent}
             */
            const equippable = source.getComponent(EntityComponentTypes.Equippable);

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    source.addEffect("strength", 10 * 20, { showParticles: false });
                    source.addEffect("speed", 10 * 20, { showParticles: false });
                }
            }
        },
        onHitEntity: (iEvent) => {
            const { hitEntity, attackingEntity } = iEvent;

            const direction = attackingEntity.getViewDirection();

            attackingEntity.playSound("chainsaw.attack", {
                x: attackingEntity.location.x + direction.x,
                y: attackingEntity.location.y + 1.5 + direction.y,
                z: attackingEntity.location.z + direction.z,
            });
        },
    });

    const shoot_baloon = event.itemComponentRegistry.registerCustomComponent("px_hc:shoot_baloon", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            /**
             * @type {Container}
             */
            const inventory = source.getComponent(EntityComponentTypes.Inventory)?.container;

            /**
             * @type {EntityEquippableComponent}
             */
            const equippable = source.getComponent(EntityComponentTypes.Equippable);

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    source.playSound("clown.attack_1", { volume: 1.0 });

                    const direction = source.getViewDirection();

                    const baloon = source.dimension.spawnEntity(Entities.baloon_projectile_player, {
                        x: source.location.x + direction.x,
                        y: source.location.y + 1.5 + direction.y,
                        z: source.location.z + direction.z,
                    });

                    baloon.setDynamicProperty("shooted_by_player", true);

                    /**
                     * @type {EntityProjectileComponent}
                     */
                    const projectile = baloon.getComponent(EntityComponentTypes.Projectile);
                    projectile.gravity = 0.01;

                    projectile.shoot({
                        x: direction.x,
                        y: direction.y,
                        z: direction.z,
                    });
                }
            }
        },
    });
    const summon_clones = event.itemComponentRegistry.registerCustomComponent(
        "px_hc:summon_clones",
        {
            onUse: (iEvent) => {
                const { source, itemStack } = iEvent;

                const entityCount = 3;

                const cooldown = itemStack.getComponent("minecraft:cooldown");

                if (cooldown) {
                    const cooldownTicks = cooldown?.cooldownTicks;
                    const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                    if (remainingCooldown == cooldownTicks - 1) {
                        cooldown.startCooldown(source);

                        for (let e = 0; e < entityCount; e++) {
                            const jester = source.dimension.spawnEntity(Entities.jester_clone, {
                                x: source.location.x + getRandomBetween(-3, 3),
                                y: source.location.y,
                                z: source.location.z + getRandomBetween(-3, 3),
                            });

                            jester.triggerEvent("clown:passive");
                        }
                    }
                }
            },
        }
    );
    const summon_random_boss = event.itemComponentRegistry.registerCustomComponent(
        "px_hc:summon_boss",
        {
            onUse: (iEvent) => {
                const { source, itemStack } = iEvent;

                const clowns = Object.values(Clowns);

                const excludedClowns = [
                    "px_hc:jester",
                    "px_hc:jester_clone",
                    "px_hc:illusionist",
                    "px_hc:ringmaster",
                ];

                const allowedClowns = clowns.filter((e) => !excludedClowns.includes(e));

                const cooldown = itemStack.getComponent("minecraft:cooldown");

                if (cooldown) {
                    const cooldownTicks = cooldown?.cooldownTicks;
                    const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                    const random = getRandomBetween(0, allowedClowns.length - 1);

                    if (remainingCooldown == cooldownTicks - 1) {
                        cooldown.startCooldown(source);

                        source.playSound("ringmaster.summon", { volume: 1.0 });

                        source.dimension.spawnParticle("px_hc:smoke", {
                            x: source.location.x,
                            y: source.location.y,
                            z: source.location.z,
                        });

                        const randomClown = allowedClowns[random];

                        const clown = source.dimension.spawnEntity(randomClown, {
                            x: source.location.x + getRandomBetween(-3, 3),
                            y: source.location.y,
                            z: source.location.z + getRandomBetween(-3, 3),
                        });

                        clown.triggerEvent("clown:passive");
                    }
                }
            },
        }
    );
    const fire_throw = event.itemComponentRegistry.registerCustomComponent("px_hc:fire_throw", {
        onUse: (iEvent) => {
            const { source, itemStack } = iEvent;

            const cooldown = itemStack.getComponent("minecraft:cooldown");

            if (cooldown) {
                const cooldownTicks = cooldown?.cooldownTicks;
                const remainingCooldown = cooldown?.getCooldownTicksRemaining(source);

                if (remainingCooldown == cooldownTicks - 1) {
                    cooldown.startCooldown(source);

                    const viewDirection = source.getViewDirection();

                    const size = 8;

                    const entities = source.dimension.getEntities({
                        location: {
                            x: source.location.x + viewDirection.x,
                            y: source.location.y,
                            z: source.location.z + viewDirection.z,
                        },
                        volume: {
                            x: viewDirection.x * size,
                            y: 2,
                            z: viewDirection.z * size,
                        },
                        excludeTypes: ["minecraft:item", "minecraft:xp_orb"],
                    });

                    source.playSound("pyro.attack_2");

                    source.dimension.spawnParticle("px_hc:fire", {
                        x: source.location.x + viewDirection.x * 1.5,
                        y: source.location.y + 1.5,
                        z: source.location.z + viewDirection.z * 1.5,
                    });

                    for (let mob of entities) {
                        if (mob !== source) {
                            mob?.applyDamage(8, {
                                cause: EntityDamageCause.entityAttack,
                                damagingEntity: source,
                            });
                            mob?.setOnFire(5);
                        }
                    }
                }
            }
        },
    });
});

system.runInterval(() => {
    const players = world.getAllPlayers();

    for (let player of players) {
        /**
         * @type {EntityEquippableComponent}
         */
        const equippable = player.getComponent(EntityComponentTypes.Equippable);
        const mainHand = equippable.getEquipment(EquipmentSlot.Mainhand);

        const hotbarItems = getHotbarItems(player);
        const inventoryItems = getInventoryItems(player);

        const hasReplaceableItem = hotbarItems.some((item) =>
            replaceableHotbarItems.has(item.itemId)
        );
        const hasReplaceableInventoryItem = inventoryItems.some((item) =>
            replaceableItems.has(item.itemId)
        );

        if (hasReplaceableItem) {
            replaceHotbarItems(player, replaceableHotbarItems);
        }
        if (hasReplaceableInventoryItem) {
            replaceInventoryItems(player, replaceableItems);
        }

        /*   if (!player.isEmoting) {
            if (mainHand) {
                if (Items.has(mainHand.typeId)) {
                    player.playAnimation("animation.px_hc.held_item", {
                        nextState: "none",
                        blendOutTime: 99,
                    });
                } else {
                    player.playAnimation("animation.px_hc.held_item_default", {
                        nextState: "none",
                        blendOutTime: 0,
                    });
                }
            } else {
                player.playAnimation("animation.px_hc.held_item_default", {
                    nextState: "none",
                    blendOutTime: 0,
                });
            }
        } */
    }
}, 0 * 20);

/**
 *
 * @param {Player} player
 */
function screamParticle(player) {
    const direction = player.getViewDirection();

    const directionX = Math.round(direction.x);
    const directionY = Math.round(direction.y);
    const directionZ = Math.round(direction.z);

    const rotationMap = new Map([
        ["[0,0,1]", { particle: "px_hc:scream_n" }],
        ["[0,0,-1]", { particle: "px_hc:scream_s" }],
        ["[1,0,0]", { particle: "px_hc:scream_w" }],
        ["[-1,0,0]", { particle: "px_hc:scream_e" }],
        ["[1,0,1]", { particle: "px_hc:scream_nw" }],
        ["[-1,0,-1]", { particle: "px_hc:scream_se" }],
        ["[-1,0,1]", { particle: "px_hc:scream_ne" }],
        ["[1,0,-1]", { particle: "px_hc:scream_sw" }],
    ]);
    const rotation = [directionX, 0, directionZ];

    player.dimension.spawnParticle(rotationMap.get(JSON.stringify(rotation))?.particle ?? "", {
        x: player.location.x + direction.x,
        y: player.location.y + 1.5 + direction.y,
        z: player.location.z + direction.z,
    });

    //console.warn(`Direction: [x: ${directionX}, y: ${directionY}, z: ${directionZ}]`);
}
