import { world, Player, ItemStack, GameMode, Entity } from "@minecraft/server";
import {
  axeConvertBlockIds,
  hoeConvertBlockIds,
  shovelConvertBlockIds,
} from "lazuli-game-data";
import {
  getEquipmentItem,
  consumeDurability,
  setEquipmentItem,
} from "lazuli-utils";

/**
 * Dispose the item when entity use it.
 * @param durability The durability to reduce.
 * @param item The item to be used.
 * @param entity The entity that used the item.
 * @param event
 */
function disposeItem(
  durability: number,
  item: ItemStack,
  entity: Entity,
  event?: (holder: Entity, item: ItemStack) => void
): void {
  const newItem = consumeDurability(item, durability, entity);
  setEquipmentItem(entity, newItem);
  if (!newItem && event) {
    event(entity, item);
  }
}


/**
 * Define a tool tag.
 * @category Need Registry
 */
export class ToolTag {
  constructor(
    /**
     * The tool tag.
     */
    public tag: string,
    /**
     * Additional options of the tool.
     */
    public options?: ToolOptions
  ) {}
  /**
   * Automatically consume durability for the tools.
   */
  listeningDurability() {
    world.afterEvents.playerBreakBlock.subscribe((event) => {
      const [PLAYER, ITEM] = [event.player, getEquipmentItem(event.player)];
      if (ITEM?.hasTag(this.tag)) {
        if (
          PLAYER.getGameMode() === GameMode.survival ||
          PLAYER.getGameMode() === GameMode.adventure
        ) {
          disposeItem(1, ITEM, PLAYER, this.options?.destroyedAfterEvents);
        }
      }
    });
    world.afterEvents.entityHitEntity.subscribe((event) => {
      const [ENTITY, ITEM] = [
        event.damagingEntity,
        getEquipmentItem(event.damagingEntity),
      ];
      if (ITEM?.hasTag(this.tag)) {
        if (ENTITY instanceof Player) {
          if (
            ENTITY.getGameMode() === GameMode.survival ||
            ENTITY.getGameMode() === GameMode.adventure
          ) {
            disposeItem(2, ITEM, ENTITY, this.options?.destroyedAfterEvents);
          }
        } else {
          disposeItem(2, ITEM, ENTITY, this.options?.destroyedAfterEvents);
        }
      }
    });
  }
  /**
   * Consume durability when the tool use on dirts.
   */
  listeningShovelDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (
        ITEM.hasTag(this.tag) &&
        shovelConvertBlockIds.includes(BLOCK.typeId)
      ) {
        /**
         * 锹压地的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("step.grass");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
  listeningHoeDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (ITEM.hasTag(this.tag) && hoeConvertBlockIds.includes(BLOCK.typeId)) {
        /**
         * 锹压地的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("step.gravel");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
  /**
   * Consume durability when the tool use on logs.
   */
  listeningAxeDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (ITEM.hasTag(this.tag) && axeConvertBlockIds.includes(BLOCK.typeId)) {
        /**
         * 斧剥皮的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("use.wood");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
}

/**
 * Define a tool.
 * @category Need Registry
 */
export class ToolItem {
  constructor(
    /**
     * Item of the weapon.
     */
    public item: ItemStack,
    /**
     * Additional options of the tool.
     */
    public options?: ToolOptions
  ) {
    this.listeningDurability();
    if (this.options?.type === ToolType.shovel) {
      this.listeningShovelDurability();
    }
    if (this.options?.type === ToolType.axe) {
      this.listeningAxeDurability();
    }
  }
  /**
   * Automatically consume durability for the tools.
   */
  listeningDurability() {
    world.afterEvents.playerBreakBlock.subscribe((event) => {
      const [PLAYER, ITEM] = [event.player, getEquipmentItem(event.player)];
      if (ITEM?.typeId === this.item.typeId) {
        if (
          PLAYER.getGameMode() === GameMode.survival ||
          PLAYER.getGameMode() === GameMode.adventure
        ) {
          disposeItem(2, ITEM, PLAYER, this.options?.destroyedAfterEvents);
        }
      }
    });
    world.afterEvents.entityHitEntity.subscribe((event) => {
      const [ENTITY, ITEM] = [
        event.damagingEntity,
        getEquipmentItem(event.damagingEntity),
      ];
      if (ITEM?.typeId === this.item.typeId) {
        if (ENTITY instanceof Player) {
          if (
            ENTITY.getGameMode() === GameMode.survival ||
            ENTITY.getGameMode() === GameMode.adventure
          ) {
            disposeItem(2, ITEM, ENTITY, this.options?.destroyedAfterEvents);
          }
        } else {
          disposeItem(2, ITEM, ENTITY, this.options?.destroyedAfterEvents);
        }
      }
    });
  }
  listeningHoeDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (
        ITEM.typeId === this.item.typeId &&
        hoeConvertBlockIds.includes(BLOCK.typeId)
      ) {
        /**
         * 锹压地的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("step.gravel");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
  /**
   * Consume durability when the tool use on dirts.
   */
  listeningShovelDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (
        ITEM.typeId === this.item.typeId &&
        shovelConvertBlockIds.includes(BLOCK.typeId)
      ) {
        /**
         * 锹压地的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("step.grass");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
  /**
   * Consume durability when the tool use on logs.
   */
  listeningAxeDurability() {
    world.afterEvents.itemUseOn.subscribe((event) => {
      const [ITEM, BLOCK, PLAYER] = [
        event.itemStack,
        event.block,
        event.source,
      ];
      if (
        ITEM.typeId === this.item.typeId &&
        axeConvertBlockIds.includes(BLOCK.typeId)
      ) {
        /**
         * 斧剥皮的事件
         */
        const newItem = consumeDurability(ITEM, 1, PLAYER);
        setEquipmentItem(PLAYER, newItem);
        PLAYER.playSound("use.wood");
        if (!newItem && this.options?.destroyedAfterEvents) {
          this.options?.destroyedAfterEvents(PLAYER, ITEM);
        }
      }
    });
  }
}

/**
 * Type of the tool.
 */
export enum ToolType {
  /**
   * Define the tool is an axe.
   */
  axe,
  /**
   * Define the tool is a shovel.
   */
  shovel,
  /**
   * Define the tool is a hoe.
   */
  hoe,
  /**
   * Define the tool is a pickaxe.
   */
  pickaxe,
  /**
   * Define a custom tool.
   */
  custom,
}

export interface ToolOptions {
  /**
   * Type of the tool.
   */
  type?: ToolType;
  /**
   * Trigger events when the tool has been destroyed.
   */
  destroyedAfterEvents?: (holder: Entity, item: ItemStack) => void;
}
