import {
  Container,
  ContainerSlot,
  Dimension,
  Entity,
  EntityEquippableComponent,
  EntityInventoryComponent,
  EntityRideableComponent,
  EquipmentSlot,
  ItemStack,
  system,
  world,
} from "@minecraft/server";
import { log } from "./Logger";
const RUNNING_ENTITIES = {},
  CREATORS = {},
  LOG_TAG = "EntityRuntime";
function gc() {
  const t = [];
  Object.keys(RUNNING_ENTITIES).forEach((e) => {
    Date.now() - RUNNING_ENTITIES[e].time >= 2e3 && t.push(e);
  });
  for (let e of t) delete RUNNING_ENTITIES[e];
}
class RuntimeItem {
  time = 0;
  data = null;
  entity = null;
}
export class EntityObjectComponent {
  getObject() {
    return this.__object;
  }
  getEntity() {
    return this.getObject().getEntity();
  }
  onTick() {}
  onUpdate() {}
  dropComponent() {
    const t = this.getObject();
    Boolean(t) && t.removeComponent(this);
  }
}
export class EntityObject {
  updateInterval = 20;
  data = {};
  #t = [];
  constructor() {
    const t = this;
    this.data = new Proxy(
      {},
      {
        get: (e, n, i) => t.getEntity().getDynamicProperty(n),
        set: (e, n, i, o) => (t.getEntity().setDynamicProperty(n, i), !0),
        has: (e, n) => t.getEntity().getDynamicPropertyIds().includes(n),
      }
    );
  }
  remRot() {
    let t = this.getEntity(),
      e = t.dimension
        .getPlayers({ closest: !0, location: t.location })[0]
        .getComponent(EntityEquippableComponent.componentId)
        .getEquipment(EquipmentSlot.Mainhand);
    return Boolean(e)
      ? "dlvn_rj:remove" == e.typeId
        ? (t.triggerEvent("dlvn_rj:killme"), 1)
        : "dlvn_rj:rotate" == e.typeId
        ? (t.triggerEvent("dlvn_rj:rotate"), 1)
        : 0
      : 0;
  }
  onTick() {}
  onUpdate() {}
  getRiders() {
    try {
      return this.getEntity().getComponent(EntityRideableComponent.componentId).getRiders() ?? [];
    } catch (t) {
      return [];
    }
  }
  getInventory() {
    return this.getEntity()?.getComponent(EntityInventoryComponent.componentId)?.container;
  }
  getInventoryItems() {
    const t = this.getInventory();
    if (t) {
      const e = [];
      for (let n = 0; n < t.size; n++) {
        const i = t.getSlot(n);
        i && i.hasItem() && e.push(i);
      }
      return e;
    }
  }
  getInventorySlots() {
    const t = this.getInventory();
    if (t) {
      const e = [];
      for (let n = 0; n < t.size; n++) {
        const i = t.getSlot(n);
        e.push(i);
      }
      return e;
    }
  }
  getEquipament(t) {
    return this.getEntity()
      ?.getComponent(EntityEquippableComponent.componentId)
      ?.getEquipmentSlot(t);
  }
  getComponents() {
    return this.#t;
  }
  addComponent(t) {
    (t.__object = this), this.#t.includes(t) || this.#t.push(t);
  }
  removeComponent(t) {
    "string" == typeof t && (t = this.#t.filter((e) => e.id == t)[0]),
      t.__object == this && (t.__object = void 0),
      (this.#t = this.#t.filter((e) => e != t));
  }
  hasRiders() {
    return this.getRiders().length > 0;
  }
  isSneaking() {
    return this.getEntity().isSneaking;
  }
  getEntity() {
    return this.__item.entity;
  }
  getActiveTime() {
    return this.getEntity().getDynamicProperty("__lifetime__") ?? 0;
  }
  setProperty(t, e) {
    this.getEntity().setProperty(t, e);
  }
  getProperty(t) {
    try {
      return this.getEntity().getProperty(t);
    } catch (t) {
      return;
    }
  }
}
export class EntityRuntime {
  static register(t, e) {
    CREATORS[t] = e;
  }
  static remove(t) {
    delete CREATORS[t];
  }
  static invokeEvent(t, e, ...n) {
    t && t.isValid() && t.typeId in CREATORS && executeSafeEvent(t, e, ...n);
  }
  static getObjectFor(t) {
    return t && t.isValid() ? RUNNING_ENTITIES[t.id] : void 0;
  }
}
function executeEvent(t, e, ...n) {
  try {
    if (!(t.id in RUNNING_ENTITIES)) {
      const e = new RuntimeItem();
      (e.time = Date.now()),
        (e.entity = t),
        (e.data = new CREATORS[t.typeId]()),
        (e.data.__item = e),
        (RUNNING_ENTITIES[t.id] = e),
        executeSafeEvent(t, "create");
    }
    const i = RUNNING_ENTITIES[t.id];
    (i.entity = t), (i.time = Date.now());
    const o = i.data;
    if (e in o)
      try {
        o[e](...n);
      } catch (t) {
        log(LOG_TAG, "Error on call entity function ", e, t);
      }
    for (let t of o.getComponents())
      if (((t.__object = o), e in t))
        try {
          t[e](...n);
        } catch (t) {
          log(LOG_TAG, "Error on call entity function on component", e, error);
        }
  } catch (t) {}
}
function parseEventName(t) {
  (t += "").includes(":") && (t = t.split(":")[1]), (t = t.toLowerCase());
  let e = "";
  for (let n = 0; n < t.length; n++) {
    const i = t.charAt(n);
    "abcdefghijklmnopqrstuvwxyz".includes(i) ? (e += i) : e.endsWith(".") || (e += ".");
  }
  let n = "";
  return (
    e.split(".").forEach((t) => {
      t.length > 1 && (n += t.charAt(0).toUpperCase() + t.substring(1).toLowerCase());
    }),
    n
  );
}
function executeSafeEvent(t, e, ...n) {
  const i = "on" + parseEventName(e);
  t && t.isValid() && t.typeId in CREATORS && executeEvent(t, i, ...n);
}
system.runInterval(() => {
  let t = {};
  world.getAllPlayers().forEach((e) => (t[e.dimension.id] = e.dimension));
  let e = [];
  for (let n of Object.values(t)) {
    n.getEntities().forEach((t) => {
      t && t.isValid() && t.typeId in CREATORS && e.push(t);
    });
  }
  for (let t of e)
    try {
      const e = (t.getDynamicProperty("__lifetime__") ?? -1) + 1;
      t.setDynamicProperty("__lifetime__", e), executeEvent(t, "onTick");
      e % RUNNING_ENTITIES[t.id].data.updateInterval == 0 && executeEvent(t, "onUpdate");
    } catch (t) {}
  (e = []), gc();
}),
  world.afterEvents.entityHitEntity.subscribe((t) => {
    executeSafeEvent(t.damagingEntity, "hit", t.hitEntity),
      executeSafeEvent(t.hitEntity, "damage", t.damagingEntity);
  }),
  system.afterEvents.scriptEventReceive.subscribe((t) => {
    if (t.sourceEntity && t.sourceEntity.isValid() && t.sourceEntity.typeId in CREATORS)
      try {
        let e = t.message;
        for (; e.includes("  "); ) e = e.replace("  ", " ");
        e = e.split(" ");
        let n = [];
        for (let t of e)
          t.replace(".", "").match(/^\d*$/)
            ? (e = parseFloat(t))
            : "true" == t || "false" == t
            ? n.push("true" == t)
            : n.push(t);
        executeSafeEvent(t.sourceEntity, t.id, ...n);
      } catch (t) {
        log(LOG_TAG, "Error on process script event", t);
      }
  }),
  world.afterEvents.entityLoad.subscribe((t) => executeSafeEvent(t.entity, "load")),
  world.beforeEvents.entityRemove.subscribe((t) => executeSafeEvent(t.removedEntity, "remove")),
  world.afterEvents.itemUse.subscribe((t) => executeSafeEvent(t.source, "item_use", t.itemStack)),
  world.afterEvents.dataDrivenEntityTrigger.subscribe((t) => executeSafeEvent(t.entity, t.eventId)),
  world.afterEvents.entitySpawn.subscribe((t) => executeSafeEvent(t.entity, "spawn")),
  world.afterEvents.entityHitBlock.subscribe((t) =>
    executeSafeEvent(t.damagingEntity, "hit_block", t.hitBlock)
  ),
  world.afterEvents.entityHurt.subscribe((t) =>
    executeSafeEvent(t.hurtEntity, "hurt", t.damage, t.damageSource)
  ),
  world.afterEvents.projectileHitEntity.subscribe((t) => {
    executeSafeEvent(t.projectile, "hit", t.getEntityHit().entity),
      executeSafeEvent(t.projectile, "projectile_hit", t.location);
  }),
  world.afterEvents.projectileHitBlock.subscribe((t) => {
    executeSafeEvent(t.projectile, "hit_block", t.getBlockHit().block),
      executeSafeEvent(t.projectile, "projectile_hit", t.location);
  });
