import { world, system } from "@minecraft/server";

const structureDef = {
  IsThatTheGrimReaper: {
    triggerBlock: "minecraft:skull",
    summonOffset: { x: 0, y: -2, z: 0 },
    summonDelayTicks: 1,
    shapes: [
      {
        default: "arcedge_horr:spooky_stone",
        blocks: [
          { offset: { x: 0, y: -2, z: 0 } },
          { offset: { x: 0, y: -1, z: 0 }, typeId: "minecraft:bone_block" },
          { offset: { x: 1, y: -2, z: 0 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 0, y: -2, z: 1 }, typeId: "minecraft:soul_sand" },
          { offset: { x: -1, y: -2, z: 0 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 0, y: -2, z: -1 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 1, y: -1, z: 0 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 0, y: -1, z: 1 }, typeId: "minecraft:soul_torch" },
          { offset: { x: -1, y: -1, z: 0 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 0, y: -1, z: -1 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 1, y: -2, z: -1 }, typeId: "minecraft:obsidian" },
          { offset: { x: -1, y: -2, z: -1 }, typeId: "minecraft:obsidian" },
          { offset: { x: -1, y: -2, z: 1 }, typeId: "minecraft:obsidian" },
          { offset: { x: 1, y: -2, z: 1 }, typeId: "minecraft:obsidian" },
        ],
      },
    ],
  },
  IsThatTheGrimReaper2: {
    triggerBlock: "minecraft:wither_skeleton_skull",
    summonOffset: { x: 0, y: -2, z: 0 },
    summonDelayTicks: 1,
    shapes: [
      {
        default: "arcedge_horr:spooky_stone",
        blocks: [
          { offset: { x: 0, y: -2, z: 0 } },
          { offset: { x: 0, y: -1, z: 0 }, typeId: "minecraft:bone_block" },
          { offset: { x: 1, y: -2, z: 0 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 0, y: -2, z: 1 }, typeId: "minecraft:soul_sand" },
          { offset: { x: -1, y: -2, z: 0 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 0, y: -2, z: -1 }, typeId: "minecraft:soul_sand" },
          { offset: { x: 1, y: -1, z: 0 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 0, y: -1, z: 1 }, typeId: "minecraft:soul_torch" },
          { offset: { x: -1, y: -1, z: 0 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 0, y: -1, z: -1 }, typeId: "minecraft:soul_torch" },
          { offset: { x: 1, y: -2, z: -1 }, typeId: "minecraft:obsidian" },
          { offset: { x: -1, y: -2, z: -1 }, typeId: "minecraft:obsidian" },
          { offset: { x: -1, y: -2, z: 1 }, typeId: "minecraft:obsidian" },
          { offset: { x: 1, y: -2, z: 1 }, typeId: "minecraft:obsidian" },
        ],
      },
    ],
  },
};

function setBlocks(player, locations) {
  for (const { x, y, z } of locations) {
    player.runCommand(`setblock ${x} ${y} ${z} air [] replace`);
  }
}

world.afterEvents.playerPlaceBlock.subscribe((event) => {
  const player = event.player;
  const block = event.block;
  const dim = event.dimension;
  const loc = block.location;

  checkForStructs(player, block, dim, loc);
});

function checkStruct(dim, struct, loc) {
  const checkBlocks = [];

  for (const shape of struct.shapes) {
    let valid = true;

    for (const { typeId, endsWith, contains, offset } of shape.blocks) {
      const x = loc.x + offset.x;
      const y = loc.y + offset.y;
      const z = loc.z + offset.z;
      const block = dim.getBlock({ x, y, z });

      if (!block) {
        valid = false;
        break;
      }

      const blockTypeId = typeId || shape.default;

      if (
        (typeId && block.typeId !== typeId) ||
        (endsWith &&
          !endsWith.some((suffix) => block.typeId.endsWith(suffix))) ||
        (contains && !contains.some((part) => block.typeId.includes(part))) ||
        (!typeId && !endsWith && !contains && block.typeId !== blockTypeId)
      ) {
        valid = false;
        break;
      }

      checkBlocks.push({ x, y, z });
    }

    if (valid) {
      checkBlocks.push(loc);
      return checkBlocks;
    }
  }
  return null;
}

function checkForStructs(player, block, dim, loc) {
  const blockTypeId = block.typeId;

  const structEntry = Object.entries(structureDef).find(
    ([, s]) => s.triggerBlock === blockTypeId
  );

  if (structEntry) {
    const [structName, struct] = structEntry;
    const checkBlocks = checkStruct(dim, struct, loc);
    if (checkBlocks) {
      const summonOffset = struct.summonOffset;
      const summonDelay = struct.summonDelayTicks;
      const summonLoc = {
        x: loc.x + summonOffset.x,
        y: loc.y + summonOffset.y,
        z: loc.z + summonOffset.z,
      };

      if (
        blockTypeId === "minecraft:skull" &&
        structName === "IsThatTheGrimReaper"
      ) {
        system.runTimeout(() => {
          player.runCommandAsync(
            `summon arcedge_horr:b_rea ${summonLoc.x} ${summonLoc.y} ${summonLoc.z} facing @p`
          );
          player.runCommandAsync(
            `particle arcedge_horr:mob_poof ${summonLoc.x} ${summonLoc.y} ${summonLoc.z}`
          );
          player.runCommandAsync(
            `playsound arcedge_horr.misc.boom @a[r=15] ${summonLoc.x} ${summonLoc.y} ${summonLoc.z}`
          );
          player.runCommandAsync(`camerashake add @a[r=15] 0.25 1 positional`);
        }, summonDelay);
      }

      if (
        blockTypeId === "minecraft:wither_skeleton_skull" &&
        structName === "IsThatTheGrimReaper2"
      ) {
        system.runTimeout(() => {
          player.runCommandAsync(
            `summon arcedge_horr:b_rea ${summonLoc.x} ${summonLoc.y} ${summonLoc.z} facing @p`
          );
          player.runCommandAsync(
            `particle arcedge_horr:mob_poof ${summonLoc.x} ${summonLoc.y} ${summonLoc.z}`
          );
          player.runCommandAsync(
            `playsound arcedge_horr.misc.boom @a[r=15] ${summonLoc.x} ${summonLoc.y} ${summonLoc.z}`
          );
          player.runCommandAsync(`camerashake add @a[r=15] 0.25 1 positional`);
        }, summonDelay);
      }

      setBlocks(player, checkBlocks);
    }
  }
}
