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

// Map of hanging vine block types to their corresponding drop item type IDs.
// Extend this mapping as needed.
const dropMapping = {
  "hb_hr:black_vine": "hb_hr:black_vine",
  "hb_hr:cursed_vine": "hb_hr:cursed_vine",
};

export function hangingVine() {
  // Register the custom block component for hanging vines when the world initializes.
  world.beforeEvents.worldInitialize.subscribe((eventData) => {
    eventData.blockComponentRegistry.registerCustomComponent("hb_hr:hanging_vine", {
      // beforeOnPlayerPlace validates the placement of a hanging vine block.
      onPlace(event) {
        const { block } = event;
        const loc = block.location;
        // Check that there is room above (the support block) since vines hang from above.
        if (loc.y + 1 >= block.dimension.heightRange.max) {
          event.cancel = true;
          return;
        }
        let blockAbove;
        let blockBelow;
        try {
          blockAbove = block.above();
          blockBelow = block.below();
        } catch (e) {}
        // Valid support: either the block above is another vine or a non‑air, non‑liquid block.
        if (!blockAbove || (!blockAbove.hasTag("hb_hr:hanging_vine") && (blockAbove.isAir || blockAbove.isLiquid))) {
          event.cancel = true;
          if (!block) return;
          if (blockBelow.typeId == block.typeId) {
            system.run(() => {
              block.setPermutation(block.permutation.withState("hb_hr:tip", false));
            });
          }
          return;
        }
        // If the block above is already a vine, update its tip state to false.
        if (blockAbove.typeId == block.typeId) {
          system.run(() => {
            blockAbove.setPermutation(blockAbove.permutation.withState("hb_hr:tip", false));
          });
        }
        // Valid support: either the block above is another vine or a non‑air, non‑liquid block.
        if (!blockBelow || (!blockBelow.hasTag("hb_hr:hanging_vine") && (blockBelow.isAir || blockBelow.isLiquid))) {
          event.cancel = true;
          return;
        }
        // If the block above is already a vine, update its tip state to false.
        if (blockBelow.typeId == block.typeId) {
          system.run(() => {
            block.setPermutation(block.permutation.withState("hb_hr:tip", false));
          });
        }
      },
      beforeOnPlayerPlace(event) {
        const { block, dimension, permutationToPlace } = event;
        const loc = block.location;
        // Check that there is room above (the support block) since vines hang from above.
        if (loc.y + 1 >= dimension.heightRange.max) {
          event.cancel = true;
          return;
        }
        let blockAbove;
        try {
          blockAbove = block.above();
        } catch (e) {}
        // Valid support: either the block above is another vine or a non‑air, non‑liquid block.
        if (!blockAbove || (!blockAbove.hasTag("hb_hr:hanging_vine") && (blockAbove.isAir || blockAbove.isLiquid))) {
          event.cancel = true;
          return;
        }
        // If the block above is already a vine, update its tip state to false.
        if (blockAbove.typeId == permutationToPlace.type.id) {
          system.run(() => {
            blockAbove.setPermutation(blockAbove.permutation.withState("hb_hr:tip", false));
          });
        }
      },
      onRandomTick(event) {
        const block = event.block;
        const dimension = block.dimension;
        const loc = block.location;

        // Check for valid support above.
        // Valid support is either another vine block or a non-air, non-liquid block.
        const perm = block.permutation;
        const blockface = perm.getState("minecraft:block_face");
        if (blockface) {
          // Define opposite faces
          const opposite = {
            west: "east",
            east: "west",
            north: "south",
            south: "north",
          };

          const opp = opposite[blockface];
          if (opp) {
            // dynamically call block.east(), block.north(), etc.
            const adjacent = block[opp]();
            // if there's no block there, or it's air, destroy the vine
            if (!adjacent || (adjacent.typeId === "minecraft:air" && block.above().typeId != block.typeId)) {
              destroyVineChain(block);
              return;
            }
          }
        }

        // Growth logic: if the current vine block is the tip, attempt to grow downward.
        // Vines hang downward so we check below.
        const isTip = perm.getState("hb_hr:tip");
        if (isTip) {
          // Maximum vine chain length check.
          // Count contiguous vine blocks upward from this tip.
          let chainLength = 1; // Count this tip block.
          let currentBlock = block;
          try {
            let aboveBlock = currentBlock.above();
            // While the block above is a vine of the same type, count it.
            while (aboveBlock && aboveBlock.hasTag("hb_hr:hanging_vine") && aboveBlock.typeId === block.typeId) {
              chainLength++;
              currentBlock = aboveBlock;
              aboveBlock = currentBlock.above();
            }
          } catch (e) {}
          // If the chain is already at maximum length (3), do not grow further.
          if (chainLength >= 4) {
            return;
          }

          if (loc.y - 1 < dimension.heightRange.min) {
            // Ensure we are within the world’s lower bound.
            return;
          }
          let blockBelow;
          try {
            blockBelow = block.below();
          } catch (e) {}
          if (blockBelow && blockBelow.isAir && !blockBelow.isLiquid) {
            if (block.typeId.includes("cursed")) return;
            const newStatesBlock = {
              "hb_hr:tip": false,
              "minecraft:block_face": block.permutation.getState("minecraft:block_face"),
            };
            const newPermBlock = BlockPermutation.resolve(block.typeId, newStatesBlock);
            block.setPermutation(newPermBlock);

            const newStatesBlockBelow = {
              "hb_hr:tip": true,
              "minecraft:block_face": block.permutation.getState("minecraft:block_face"),
            };
            const newPermBlockBelow = BlockPermutation.resolve(block.typeId, newStatesBlockBelow);
            blockBelow.setPermutation(newPermBlockBelow);
          }
        }
      },
    });
  });

  // Handle player breaking events for vine blocks.
  world.beforeEvents.playerBreakBlock.subscribe((data) => {
    const block = data.block;
    const northBlock = block.north();
    const eastBlock = block.east();
    const southBlock = block.south();
    const westBlock = block.west();
    if (northBlock && northBlock.hasTag("hb_hr:hanging_vine") && !northBlock.above().hasTag("hb_hr:hanging_vine")) destroyVineChain(northBlock);
    if (eastBlock && eastBlock.hasTag("hb_hr:hanging_vine") && !eastBlock.above().hasTag("hb_hr:hanging_vine")) destroyVineChain(eastBlock);
    if (southBlock && southBlock.hasTag("hb_hr:hanging_vine") && !southBlock.above().hasTag("hb_hr:hanging_vine")) destroyVineChain(southBlock);
    if (westBlock && westBlock.hasTag("hb_hr:hanging_vine") && !westBlock.above().hasTag("hb_hr:hanging_vine")) destroyVineChain(westBlock);
    // Only process blocks that are part of the vine chain.
    if (!block.hasTag("hb_hr:hanging_vine")) return; // Only process hanging vine blocks.

    data.cancel = true; // Cancel normal break logic.
    const player = data.player;
    const equippable = player.getComponent("equippable");
    const mainhandItem = equippable && equippable.getEquipment(EquipmentSlot.Mainhand);
    const usingShears = mainhandItem && mainhandItem.typeId === "minecraft:shears";

    // If using shears, drop the corresponding vine item.
    const dropItemId = dropMapping[block.typeId];
    let blockItem;
    if (mainhandItem?.getComponent("enchantable")?.getEnchantment("silk_touch") != undefined) {
      // world.sendMessage("spawn item");
      system.run(() => {
        blockItem = new ItemStack(dropItemId);
        block.dimension.spawnItem(blockItem, block.location);
      });
    } else if ((mainhandItem == undefined && dropItemId) || (!usingShears && dropItemId)) {
      system.run(() => {
        // world.sendMessage("maybe drop sapling");
        if (dropItemId.includes("cursed")) {
          if (Math.random() < 0.3) {
            blockItem = new ItemStack("hb_hr:cursed_sapling");
            block.dimension.spawnItem(blockItem, block.location);
          }
        }
      });
    } else if (usingShears && dropItemId) {
      const drop = new ItemStack(dropItemId, 1);
      system.run(() => {
        // world.sendMessage("spawn shears");
        block.dimension.spawnItem(drop, block.location);
      });
    }
    let blockAbove;
    try {
      blockAbove = block.above();
    } catch (e) {}
    system.run(() => {
      if (blockAbove && blockAbove.hasTag("hb_hr:hanging_vine")) {
        blockAbove.setPermutation(blockAbove.permutation.withState("hb_hr:tip", true));
      }
      block.dimension.runCommand("fill " + block.location.x + " " + block.location.y + " " + block.location.z + " " + block.location.x + " " + block.location.y + " " + block.location.z + " air destroy");
    });
    // For non-tip vine blocks (or when there is no vine above), destroy the entire vine chain from this block downward.
    destroyVineChain(block);
  });

  // Handle explosions that affect vine blocks.
  world.beforeEvents.explosion.subscribe((event) => {
    const vineBlocks = event.getImpactedBlocks().filter((block) => block.permutation.hasTag("hb_hr:hanging_vine"));

    for (const block of vineBlocks) {
      const blockAbove = block.above();
      system.run(() => {
        if (blockAbove && blockAbove.hasTag("hb_hr:hanging_vine")) {
          blockAbove.setPermutation(blockAbove.permutation.withState("hb_hr:tip", true));
        }
        block.dimension.runCommand("fill " + block.location.x + " " + block.location.y + " " + block.location.z + " " + block.location.x + " " + block.location.y + " " + block.location.z + " air destroy");
      });
      system.runTimeout(() => {
        destroyVineChain(block);
      }, 1);
    }
  });

  // Helper function: destroys a contiguous vine chain starting from the given block and moving downward.
  function destroyVineChain(startBlock) {
    let currentBlock = startBlock;
    const perm = currentBlock.permutation;
    let adjacent;
    const blockface = perm.getState("minecraft:block_face");
    if (blockface) {
      // Define opposite faces
      const opposite = {
        west: "east",
        east: "west",
        north: "south",
        south: "north",
      };

      const opp = opposite[blockface];
      if (opp) {
        // dynamically call block.east(), block.north(), etc.
        adjacent = currentBlock[opp]();
      }
    }
    if (adjacent == undefined || !adjacent) return;
    if (currentBlock.below().hasTag("hb_hr:hanging_vine") && adjacent.below().typeId == "minecraft:air") {
      // if (currentBlock.below().hasTag("hb_hr:hanging_vine")) {
      destroyVineChain(currentBlock.below());
    }
    if (currentBlock.hasTag("hb_hr:hanging_vine")) {
      system.run(() => {
        currentBlock.dimension.runCommand("fill " + currentBlock.location.x + " " + currentBlock.location.y + " " + currentBlock.location.z + " " + currentBlock.location.x + " " + currentBlock.location.y + " " + currentBlock.location.z + " air destroy");
      });
    }
  }
}
