import { world, ItemStack, GameMode } from '@minecraft/server';

// This array defines the tool items that will lose durability.
const toolTypeIds = [
    'auxillium:auxillium_pickaxe',
    'auxillium:auxillium_axe',
    'auxillium:auxillium_shovel',
    'auxillium:auxillium_hoe',
    'auxillium:cosmium_pickaxe',
    'auxillium:cosmium_axe',
    'auxillium:cosmium_shovel',
    'auxillium:cosmium_hoe',
    'auxillium:necrite_pickaxe',
    'auxillium:necrite_axe',
    'auxillium:necrite_shovel',
    'auxillium:necrite_hoe',
    'auxillium:aurorus_pickaxe',
    'auxillium:aurorus_axe',
    'auxillium:aurorus_shovel',
    'auxillium:aurorus_hoe'
];

// This array defines the weapon items that will lose durability.
const weaponTypeIds = [
    'auxillium:auxillium_sword',
    'auxillium:cosmium_sword',
    'auxillium:necrite_sword',
    'auxillium:aurorus_sword',
];

world.afterEvents.playerBreakBlock.subscribe(evd => {
    const { player, itemStackBeforeBreak: itemUsed } = evd;

    // This returns if itemUsed is undefined or if the player is in Creative.
    if (!itemUsed || player.matches({ gameMode: GameMode.creative })) return;

    if (toolTypeIds.includes(itemUsed.typeId) || weaponTypeIds.includes(itemUsed.typeId)) {
        // This retrieves the player's equippable component.
        const playerEquippableComp = player.getComponent("equippable");

        // This returns if playerEquippableComp is undefined.
        if (!playerEquippableComp) return;

        // This retrieves the enchantable component of the item.
        const itemEnchantmentComp = itemUsed.getComponent("minecraft:enchantable");
        const unbreakingLevel = itemEnchantmentComp?.getEnchantment("unbreaking")?.level ?? 0;

        // Calculates the chance of an item breaking based on its unbreaking level. This is the vanilla unbreaking formula.
        const breakChance = 100 / (unbreakingLevel + 1);
        // Generates a random chance value between 0 and 100.
        const randomizeChance = Math.random() * 100;

        // This returns if breakChance is less than randomizeChance.
        if (breakChance < randomizeChance) return;

        // This retrieves the durability component of the item.
        const itemUsedDurabilityComp = itemUsed.getComponent("durability");

        // This returns if itemUsedDurabilityComp is undefined.
        if (!itemUsedDurabilityComp) return;

        let durabilityModifier = 0;
        if (toolTypeIds.includes(itemUsed.typeId)) {
            // If the item is a tool, then it will set the durability modifier to 1.
            durabilityModifier = 1;
        } else {
            // If the item is a weapon, then it will set the durability modifier to 2.
            durabilityModifier = 2;
        }

        // This will set the new durability value.
        itemUsedDurabilityComp.damage += durabilityModifier;

        // Declares and checks if the item is out of durability
        const maxDurability = itemUsedDurabilityComp.maxDurability
        const currentDamage = itemUsedDurabilityComp.damage
        if (currentDamage >= maxDurability) {

            // If the item is out of durability, plays the item breaking sound and removes the item
            player.playSound('random.break', { pitch: 1, location: player.location, volume: 1 })
            playerEquippableComp.setEquipment("Mainhand", new ItemStack('minecraft:air', 1));
        }
        else if (currentDamage < maxDurability) {

            // This sets the item in the player's selected slot.
            playerEquippableComp.setEquipment("Mainhand", itemUsed);
        }
    }
})



// This is a Set containing a collection of valid effect types.
const validEffectTypes = new Set([
  "absorption", "bad_omen", "blindness", "clear", "conduit_power", "darkness",
  "fatal_poison", "fire_resistance", "haste", "health_boost", "hunger", 
  "instant_damage", "instant_health", "invisibility", "jump_boost", 
  "levitation", "mining_fatigue", "nausea", "night_vision", "poison", 
  "regeneration", "resistance", "saturation", "slow_falling", "slowness", 
  "speed", "strength", "village_hero", "water_breathing", "weakness", "wither"
]);

/**
 * Object array defining the tools, along with the effect(s) and modifiers assigned to them.
 * 
 * toolTypeId: string; - The typeId of the tool.
 * effects: {
 *      effectType: string; - The type of effect applied by the tool.
 *      duration: number; - The duration of the effect applied by the tool in seconds.
 *      amplifier: number; - The amplifier of the effect applied by the tool.
 *      showParticles: boolean; - Indicates whether particles are shown for the effect.
 * }[];
 * 
 */
const toolEffects  = [
  {
    toolTypeId: "auxillium:necrite_sword",
    effects: [
      { effectType: "wither", duration: 4, amplifier: 1, showParticles: true }
    ]
  },
  {
    toolTypeId: "auxillium:necrite_axe",
    effects: [
      { effectType: "wither", duration: 4, amplifier: 1, showParticles: true }
    ]
  },
  {
    toolTypeId: "auxillium:necrite_pickaxe",
    effects: [
      { effectType: "wither", duration: 3, amplifier: 1, showParticles: true }
    ]
  },
  {
    toolTypeId: "auxillium:necrite_shovel",
    effects: [
      { effectType: "wither", duration: 3, amplifier: 1, showParticles: true }
    ]
  },
  {
    toolTypeId: "auxillium:necrite_hoe",
    effects: [
      { effectType: "wither", duration: 3, amplifier: 1, showParticles: true }
    ]
  }
];

world.afterEvents.entityHitEntity.subscribe(evd => {
  const { damagingEntity, hitEntity } = evd;

  // This returns if the damagingEntity is not a player.
  if (damagingEntity.typeId !== 'minecraft:player') return;

  // This retrieves the damaging entity's equippable component.
  const playerEquippableComp = damagingEntity.getComponent("equippable");

  // This returns if playerEquippableComp is undefined.
  if (!playerEquippableComp) return;

  // This retrieves the item held by the damaging entity.
  const heldItem = playerEquippableComp.getEquipment("Mainhand");

  // This returns if heldItem is undefined.
  if (!heldItem) return;

  // This searches for a tool in the toolEffects array matching the held item.
  const foundTool = toolEffects.find(tool => tool.toolTypeId === heldItem.typeId);
  // If the tool is within the toolEffects array, then it will apply the effect(s) and modifiers assigned to the heldItem to the entity that was hit.
  if (foundTool) {
    foundTool.effects.forEach(effectInfo => {
      const { effectType, duration, amplifier, showParticles } = effectInfo;


      // This returns if effectType is invalid.
      if (!validEffectTypes.has(effectType)) return console.error(`§cError:§r §7Invalid effect type '§r${effectType}§7' detected.`);

      // This converts duration from seconds into ticks.
      const durationInTicks = 20 * duration;
      // This returns if durationValue is not within a valid range (1 to 20,000,000 ticks).
      // The error message is based on the duration in seconds.
      if (durationInTicks < 1 || durationInTicks > 20000000) return console.error(`§cError:§r §7Duration value '§r${duration}§7' is not within the valid range (0 to 1,000,000 seconds).`);

      // This returns if amplifierValue is not within a valid range (0 to 255).
      if (amplifier < 0 || amplifier > 255) return console.error(`§cError:§r §7Amplifier value '§r${amplifier}§7' is not within the valid range (0 to 255).`);

      // This returns if the showParticles value is not a boolean.
      if (typeof showParticles !== 'boolean') return console.error(`§cError:§r §7Invalid value '§r${showParticles}§7' for showParticles. It must be a boolean type (true or false).`);


      hitEntity.addEffect(effectType, durationInTicks, { amplifier, showParticles });
    });
  }
});





world.afterEvents.entityHurt.subscribe((event) => {
  const deadEntity = event.hurtEntity;
  const damageSource = event.damageSource.damagingEntity;
  const damageProjectile = event.damageSource.damagingProjectile;
  const damageDone = event.damage;

  if (!damageSource) {
    return;
  }
  if (damageSource.typeId !== 'minecraft:player') return;
  const heldItem = damageSource.getComponent("minecraft:equippable").getEquipment("Mainhand");
  if (!heldItem) return;

  if (damageSource.typeId === "minecraft:player" && heldItem.typeId === "auxillium:auxillium_bow") {
    if (!damageProjectile) {
      return;
    }
    if (damageSource.typeId === 'minecraft:player' && damageProjectile.typeId === 'minecraft:arrow') {
      deadEntity.runCommandAsync('particle auxillium:bow_shot ~~1~')
      if (damageDone < 2) deadEntity.applyKnockback(0, 0, 0, damageDone/3)
      if (damageDone >=2) deadEntity.applyKnockback(0, 0, 0, 2/3)
      
    }
  }
});