import { Entity, Player, system, world } from "@minecraft/server";
import {
  Article,
  ArticleCollection,
  Boss,
  ChapterArticle,
  ChapterQuestBook,
  CustomBlockComponent,
  CustomItemComponent,
  DurabilityLimitedProp,
  FoodItem,
  GiftItem,
  NumberLimitedProp,
  PercentGiftItem,
  Prop,
  Quest,
  QuestBoard,
  QuestBook,
  ToolItem,
  ToolTag,
  ToolType,
  WeaponItem,
  WeaponTag,
  WeightGiftItem,
} from "../..";
import { EffectData } from "lazuli-common";
import { consumeDurability, setEquipmentItem } from "lazuli-utils";

/**
 * Registried Boss
 * @param boss
 * @param entity
 */
function registriedBoss(boss: Boss, entity: Entity) {
  // Tirgger music
  if (entity.typeId === boss.typeId && boss.music) {
    const MUSIC = boss.music;
    const players: Player[] = [];
    entity.dimension
      .getEntities({
        location: entity.location,
        minDistance: 0,
        maxDistance: MUSIC.radius,
      })
      .forEach((entity) => {
        if (entity instanceof Player) {
          entity.playMusic(MUSIC.trackId, { loop: true });
          players.push(entity);
        }
      });
    world.afterEvents.entityDie.subscribe((event) => {
      if (event.deadEntity.id === entity.id) {
        players.forEach((player) => {
          player.stopMusic();
        });
      }
    });
    world.afterEvents.entityRemove.subscribe((event) => {
      if (event.removedEntityId === entity.id) {
        players.forEach((player) => {
          player.stopMusic();
        });
      }
    });
    // Start skills
    if (
      entity.typeId === boss.typeId &&
      !entity.getDynamicProperty("lazuli:start_skill")
    ) {
      entity.setDynamicProperty("lazuli:start_skill", true);
      boss.skills.forEach((skill) => {
        let handle = system.runInterval(() => {
          skill.unleashArray(
            entity.dimension.getEntities({
              location: entity.location,
              minDistance: 0,
              maxDistance: skill.radius,
            }),
            entity
          );
        }, skill.cooldownTime);
        world.afterEvents.entityDie.subscribe((event) => {
          if (event.deadEntity.id === entity.id) {
            system.clearRun(handle);
          }
        });
        world.afterEvents.entityRemove.subscribe((event) => {
          if (event.removedEntityId === entity.id) {
            system.clearRun(handle);
          }
        });
      });
    }
  }
}

/**
 * Registired Display.
 * @param item
 */
function registriedDisplay(item: ArticleCollection | QuestBoard) {
  if (item.displayCondition.default === true) {
    world.afterEvents.itemUse.subscribe((event) => {
      if (event.itemStack.typeId === item.id) item.display(event.source);
    });
  }
  if (item.displayCondition.firstLoad === true) {
    world.afterEvents.playerSpawn.subscribe((event) => {
      if (event.initialSpawn) item.display(event.player);
    });
  }
  if (item.displayCondition.itemStack) {
    world.afterEvents.itemUse.subscribe((event) => {
      if (event.itemStack.typeId === item.displayCondition.itemStack?.typeId)
        item.display(event.source);
    });
  }
  if (item.displayCondition.command) {
    const options = item.displayCondition.command;
    world.beforeEvents.chatSend.subscribe((event) => {
      if (typeof options === "string") {
        if (event.message === options) {
          event.cancel = true;
          system.runTimeout(() => {
            item.display(event.sender);
          }, 20);
        }
      } else if (options.includes(event.message)) {
        event.cancel = true;
        system.runTimeout(() => {
          item.display(event.sender);
        }, 20);
      }
    });
  }
}

export type NeedRegistiy =
  | Article
  | ArticleCollection
  | Boss
  | ChapterArticle
  | ChapterQuestBook
  | CustomBlockComponent
  | CustomItemComponent
  | FoodItem
  | GiftItem
  | Prop
  | Quest
  | QuestBoard
  | QuestBook
  | ToolItem
  | ToolTag
  | ToolType
  | WeaponItem
  | WeaponTag;

/**
 * Union register for api instances.
 * @category Registry
 */
class BaseRegister {
  private constructor() {}
  /**
   * Registry the food item.
   * @param item
   */
  static foodRegistry(item: FoodItem): void {
    world.afterEvents.itemCompleteUse.subscribe((event) => {
      const [PLAYER, ITEM] = [event.source, event.itemStack];
      if (ITEM.typeId === item.typeId) {
        item.statusEffects?.forEach((data: EffectData) => {
          PLAYER.addEffect(data.effectType, data.duration, {
            amplifier: data.amplifier,
            showParticles: data.showParticles,
          });
        });
        if (item.eatEvent) {
          item.eatEvent(event);
        }
      }
    });
  }
  /**
   * Registry the gift item.
   * @param item
   */
  static giftRegistry(item: GiftItem | WeightGiftItem | PercentGiftItem): void {
    world.afterEvents.itemUse.subscribe((event) => {
      if (event.itemStack.typeId === item.typeId) {
        setEquipmentItem(event.source);
        item.giveReward(event.source);
      }
    });
  }
  /**
   * Registry the prop item.
   * @param item
   */
  static propRegistry(item: Prop | DurabilityLimitedProp | NumberLimitedProp) {
    if (item instanceof DurabilityLimitedProp) {
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === item.typeId && item.useEvent) {
          item.useEvent(event);
          setEquipmentItem(
            event.source,
            consumeDurability(
              event.itemStack,
              item.durabilityValue,
              event.source
            )
          );
        }
      });
    } else if (item instanceof NumberLimitedProp) {
      let [MAX_USE, ONCE_CONS] = [
        item.options.maxUse,
        item.options.onceConsumed,
      ];
      if (!ONCE_CONS) ONCE_CONS = 1;
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === item.typeId && item.useEvent) {
          const [PLAYER, ITEM] = [event.source, event.itemStack];
          const VALUE = ITEM.getDynamicProperty("lazuli:use_value") as number;
          item.useEvent(event);
          if (VALUE) {
            ITEM.setDynamicProperty("lazuli:use_value", VALUE + ONCE_CONS);
          } else {
            ITEM.setDynamicProperty("lazuli:use_value", ONCE_CONS);
          }
          if (VALUE > MAX_USE) {
            setEquipmentItem(PLAYER);
          }
        }
      });
    } else {
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === item.typeId && item.useEvent) {
          item.useEvent(event);
          setEquipmentItem(event.source);
        }
      });
    }
  }
  /**
   * Registry the tool tag or item.
   * @param item
   */
  static toolRegistry(item: ToolItem | ToolTag) {
    item.listeningDurability();
    if (item.options?.type === ToolType.shovel) {
      item.listeningShovelDurability();
    }
    if (item.options?.type === ToolType.axe) {
      item.listeningAxeDurability();
    }
    if (item.options?.type === ToolType.hoe) {
      item.listeningHoeDurability();
    }
  }
  /**
   * Registry the weapon tag or item.
   * @param item
   */
  static weaponRegistry(item: WeaponItem | WeaponTag) {
    item.listeningDurability();
  }
  /**
   * Registry the article.
   * @param item
   */
  static articleRegistry(item: Article | ChapterArticle | ArticleCollection) {
    if (item instanceof ArticleCollection) {
      registriedDisplay(item);
    } else {
      world.afterEvents.itemUse.subscribe((event) => {
        if (event.itemStack.typeId === item.id) item.display(event.source);
      });
    }
  }
  /**
   * Registry the quest or quest book.
   * @param item
   */
  static questRegistry(
    item: Quest | QuestBook | ChapterQuestBook | QuestBoard
  ) {
    if (item instanceof QuestBoard) {
      registriedDisplay(item);
    } else if (item instanceof Quest) {
      if (item.options.condition.killEntity) {
        const ENTITY_DATA = item.options.condition.killEntity;
        world.afterEvents.entityDie.subscribe((event) => {
          if (
            event.deadEntity.typeId === ENTITY_DATA.typeId &&
            event.damageSource.damagingEntity instanceof Player
          ) {
            const PLAYER = event.damageSource.damagingEntity;
            if (!item.isCompleted(PLAYER)) item.complete(PLAYER);
          }
        });
      }
    } else {
      world.afterEvents.itemUse.subscribe((event) => {
        if (item.id === event.itemStack.typeId) {
          item.display(event.source);
        }
      });
    }
  }
  /**
   * Registry the job.
   * @param item
   */
  static jobRegistry(item: any) {
    throw new Error("Function not implemented.");
  }
  /**
   * Registry the boss.
   * @param item
   */
  static bossRegistry(item: Boss) {
    world.afterEvents.entitySpawn.subscribe((event) => {
      const ENTITY = event.entity;
      registriedBoss(item, ENTITY);
    });
    world.afterEvents.entityLoad.subscribe((event) => {
      const ENTITY = event.entity;
      registriedBoss(item, ENTITY);
    });
  }
  /**
   * Registry the component.
   * @param item
   */
  static componentRegistry(item: CustomItemComponent | CustomBlockComponent) {
    world.beforeEvents.worldInitialize.subscribe((event) => {
      if (item instanceof CustomItemComponent) {
        event.itemComponentRegistry.registerCustomComponent(
          item.typeId,
          item.components
        );
      } else {
        event.blockComponentRegistry.registerCustomComponent(
          item.typeId,
          item.components
        );
      }
    });
  }
}

/**
 * Union register for api instances.
 * @category Registry
 */
export class Register {
  private constructor() {}
  /**
   * Registry an instance.
   * @param instance The instance to registry.
   * @category Registry
   */
  static simpleRegistry(instance: NeedRegistiy) {
    if (instance instanceof FoodItem) {
      BaseRegister.foodRegistry(instance);
    }
    if (instance instanceof GiftItem) {
      BaseRegister.giftRegistry(instance);
    }
    if (
      instance instanceof Prop ||
      instance instanceof DurabilityLimitedProp ||
      instance instanceof NumberLimitedProp
    ) {
      BaseRegister.propRegistry(instance);
    }
    if (instance instanceof ToolItem || instance instanceof ToolTag) {
      BaseRegister.toolRegistry(instance);
    }
    if (instance instanceof WeaponItem || instance instanceof WeaponTag) {
      BaseRegister.weaponRegistry(instance);
    }
    if (instance instanceof Article || instance instanceof ArticleCollection) {
      BaseRegister.articleRegistry(instance);
    }
    if (instance instanceof Article || instance instanceof ArticleCollection) {
      BaseRegister.articleRegistry(instance);
    }
    if (
      instance instanceof Quest ||
      instance instanceof QuestBook ||
      instance instanceof ChapterQuestBook ||
      instance instanceof QuestBoard
    ) {
      BaseRegister.questRegistry(instance);
    }
    if (instance instanceof Boss) {
      BaseRegister.bossRegistry(instance);
    }
    if (
      instance instanceof CustomItemComponent ||
      instance instanceof CustomBlockComponent
    ) {
      BaseRegister.componentRegistry(instance);
    }
  }
  /**
   * Registry the instances.
   * @param instance Instances to registry.
   */
  static registry(instance: NeedRegistiy | NeedRegistiy[]) {
    if(Array.isArray(instance)){
      instance.forEach(ins=>{
        Register.simpleRegistry(ins)
      })
    }else{
      Register.simpleRegistry(instance);
    }
  }
}
