import { BlockVolume, MolangVariableMap, BlockPermutation, TimeOfDay, system, world, ItemStack, StructureAnimationMode, StructureRotation, StructureSaveMode } from "@minecraft/server";
import { getSideBlockLocations2Blocks, getSideBlockLocations, checkBelowEntityDespawn, getStringBefore, checkLocation, getEntityRotation, getLocationbyFacingBlock, stringBetween, getGamemode, getElementPosition, getElementPositionArrayWithObjects, checkBelowEntityDespawnRoof } from "./helpers";
import { cameraCutscene } from "../events/camera";
import { ActionFormData } from "@minecraft/server-ui";

const roofColors = ["white", "black", "blue", "brown", "cyan", "gray", "green", "light_blue", "light_gray", "lime", "magenta", "orange", "pink", "purple", "red", "yellow"];
const roofBlocks = ["hb_cb:t1_roof_1_block", "hb_cb:t1_roof_2_block", "hb_cb:t2_roof_2_block"];

export class roofHandler {
  static initialized = false;
  constructor() {
    this.initialize();
  }

  initialize() {
    if (!roofHandler.initialized) {
      roofHandler.initialized = true;
      this.processSubscriptions();
    } else {
    }
  }
  processSubscriptions() {
    let curTick = 0;
    const INIT_TIME = 0;

    system.runInterval(() => {
      try {
        if (curTick > INIT_TIME) {
          const allPlayers = world.getAllPlayers();
          allPlayers.forEach((player) => {
            if (!player.isValid()) return;
            else {
              try {
                checkOldRoofDespawn(player);
              } catch (error) {}
            }
          });
        }
        curTick++;
      } catch (e) {
        console.warn(e);
      }
    }, 40);
    world.afterEvents.playerInteractWithEntity.subscribe((event) => {
      const player = event.player;
      const typeId = player.typeId;
      if (typeId !== "minecraft:player") {
        return;
      }
      if (event.target.typeId.includes("hb_cb:") && event.target.typeId.includes("roof")) {
        const roof = event.target;
        const holdingItem = player.getComponent("equippable").getEquipment("Mainhand");
        if (!holdingItem) return;
        if (holdingItem.typeId.includes("minecraft:") && holdingItem.typeId.includes("dye")) {
          this.changeTentColor(player, roof, holdingItem);
        }
      } else return;
    });
    world.afterEvents.itemUse.subscribe((event) => {
      const player = event.source;
      const typeId = player.typeId;
      if (typeId !== "minecraft:player") {
        return;
      }
      const holdingItem = player.getComponent("equippable").getEquipment("Mainhand");
      if (holdingItem == undefined) {
        return;
      }
      if (holdingItem.typeId.includes("hb_cb:") && holdingItem.typeId.includes("roof")) this.spawnroof(player, holdingItem);
    });
    system.afterEvents.scriptEventReceive.subscribe((event) => {
      if (event.sourceEntity == undefined) return;
      else {
        if (event.sourceEntity.typeId.includes("hb_cb:") && event.sourceEntity.typeId.includes("roof")) {
          let roof = event.sourceEntity;
          switch (event.id) {
            case "hb_cb:despawn_tick_hit_trigger":
              if (roof.getProperty("hb_cb:despawn_tick") < 4) {
                roof.runCommand("/playanimation @s animation.hb_cb.roof.break");
                roof.runCommand("/event entity @s hb_cb:despawn_tick_hit");
              }
              break;
            case "hb_cb:roof_despawn":
              this.despawnroof(roof);
              break;
            default:
              break;
          }
        }
      }
    });
  }

  spawnroof(player, item) {
    const raycastBlock = player.getBlockFromViewDirection({ includeLiquidBlocks: false, includePassableBlocks: false, maxDistance: 8 });
    if (raycastBlock == undefined) return;
    else {
      let gamemodeCreative = getGamemode(player, 1);
      const location = raycastBlock.block.location;
      const face = raycastBlock.face;

      let placeLoc = getLocationbyFacingBlock(face, location);
      if (checkLocation(placeLoc, player, placeLoc, 2)) return;
      let yRotationEntity = getEntityRotation(player);
      let orientation;
      switch (yRotationEntity) {
        case 0:
          orientation = "north";
          break;
        case 90:
          orientation = "east";
          break;
        case 180:
          orientation = "south";
          break;
        case 270:
          orientation = "west";
          break;

        default:
          break;
      }
      const playerYaw = player.getRotation().y * (Math.PI / 180); // Convert yaw from degrees to radians
      // Get the tent's position (x, y, z)
      const roofPos = placeLoc;

      // Calculate the new position (this will place the player in front of the roof)
      const OneBlockinfrontOfRoof = {
        x: roofPos.x,
        y: roofPos.y, // Keep the same Y level as the roof (you can adjust this if needed)
        z: roofPos.z,
      };

      const twoBlocksoffsetX = Math.sin(playerYaw) * -1;
      const twoBlocksoffsetZ = -Math.cos(playerYaw) * -1;

      // Calculate the new position (this will place the player in front of the roof)
      const twoBlocksinfrontOfRoof = {
        x: roofPos.x + twoBlocksoffsetX,
        y: roofPos.y, // Keep the same Y level as the roof (you can adjust this if needed)
        z: roofPos.z + twoBlocksoffsetZ,
      };
      if (item.typeId.includes("t1")) {
        let block = world.getDimension(player.dimension.id).getBlock({ x: twoBlocksinfrontOfRoof.x, y: twoBlocksinfrontOfRoof.y + 2, z: twoBlocksinfrontOfRoof.z });
        let { leftBlock, rightBlock } = getSideBlockLocations(orientation, block);
        let leftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: leftBlock.x, y: leftBlock.y, z: leftBlock.z });
        let rightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: rightBlock.x, y: rightBlock.y, z: rightBlock.z });

        // Place all three blocks
        [block, leftBlockPlace, rightBlockPlace].forEach((blockPos) => {
          player.runCommand("/fill " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " air destroy");
          blockPos.setType("hb_cb:t1_v2_roof_1_block");
          blockPos.setPermutation(
            BlockPermutation.resolve("hb_cb:t1_v2_roof_1_block", {
              "minecraft:block_face": orientation,
            })
          );
        });
        let infrontblock = world.getDimension(player.dimension.id).getBlock({ x: OneBlockinfrontOfRoof.x, y: OneBlockinfrontOfRoof.y + 2, z: OneBlockinfrontOfRoof.z });
        const { leftBlock: leftBlock2, rightBlock: rightBlock2 } = getSideBlockLocations(orientation, infrontblock);
        leftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: leftBlock2.x, y: leftBlock2.y, z: leftBlock2.z });
        rightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: rightBlock2.x, y: rightBlock2.y, z: rightBlock2.z });

        // Place all three blocks
        [infrontblock, leftBlockPlace, rightBlockPlace].forEach((blockPos) => {
          player.runCommand("/fill " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " air destroy");
          blockPos.setType("hb_cb:t1_v2_roof_2_block");
          blockPos.setPermutation(
            BlockPermutation.resolve("hb_cb:t1_v2_roof_2_block", {
              "minecraft:block_face": orientation,
            })
          );
        });
      }
      if (item.typeId.includes("t2")) {
        // Ensure block is valid
        let block = world.getDimension(player.dimension.id).getBlock({ x: twoBlocksinfrontOfRoof.x, y: twoBlocksinfrontOfRoof.y + 2, z: twoBlocksinfrontOfRoof.z });
        if (!block) {
          // //////////world.sendMessage("Block at " + placeLoc.x + ", " + (placeLoc.y + 2) + ", " + placeLoc.z + " is undefined.");
          return;
        }

        let { leftBlock, rightBlock, nextLeftBlock, nextRightBlock } = getSideBlockLocations2Blocks(orientation, block);
        // //////////world.sendMessage("leftBlock: " + leftBlock + " rightBlock: " + rightBlock + " nextLeftBlock: " + nextLeftBlock + " nextRightBlock: " + nextRightBlock);

        // Validate block placements
        let leftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: leftBlock.x, y: leftBlock.y, z: leftBlock.z });
        let rightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: rightBlock.x, y: rightBlock.y, z: rightBlock.z });
        let nextRightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: nextRightBlock.x, y: nextRightBlock.y, z: nextRightBlock.z });
        let nextLeftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: nextLeftBlock.x, y: nextLeftBlock.y, z: nextLeftBlock.z });

        // Log the retrieved blocks
        // //////////world.sendMessage("leftBlockPlace: " + leftBlockPlace);
        // //////////world.sendMessage("rightBlockPlace: " + rightBlockPlace);
        // //////////world.sendMessage("nextRightBlockPlace: " + nextRightBlockPlace);
        // //////////world.sendMessage("nextLeftBlockPlace: " + nextLeftBlockPlace);

        // Ensure no block is undefined
        if (!leftBlockPlace || !rightBlockPlace || !nextRightBlockPlace || !nextLeftBlockPlace) {
          // //////////world.sendMessage("One of the block placements is undefined.");
          return;
        }

        // Place all blocks
        [block, leftBlockPlace, rightBlockPlace, nextLeftBlockPlace, nextRightBlockPlace].forEach((blockPos) => {
          player.runCommand("/fill " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " air destroy");
          // // //////////world.sendMessage(blockPos.x + "" + blockPos.y + "" + blockPos.z + "" + blockPos.typeId);
          blockPos.setType("hb_cb:t1_v2_roof_1_block");
          blockPos.setPermutation(
            BlockPermutation.resolve("hb_cb:t1_v2_roof_1_block", {
              "minecraft:block_face": orientation,
            })
          );
        });

        // Additional processing for OneBlockinfrontOfRoof
        let OneBlockinfrontOfRoofPlace = world.getDimension(player.dimension.id).getBlock({ x: OneBlockinfrontOfRoof.x, y: OneBlockinfrontOfRoof.y + 2, z: OneBlockinfrontOfRoof.z });
        if (!OneBlockinfrontOfRoofPlace) {
          // //////////world.sendMessage("OneBlockinfrontOfRoof is undefined.");
          return;
        }

        let { leftBlock: infrontleftBlock, rightBlock: infrontrightBlock, nextLeftBlock: infrontnextLeftBlock, nextRightBlock: infrontnextRightBlock } = getSideBlockLocations2Blocks(orientation, OneBlockinfrontOfRoofPlace);

        // Validate infront block placements
        let infrontleftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: infrontleftBlock.x, y: infrontleftBlock.y, z: infrontleftBlock.z });
        let infrontrightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: infrontrightBlock.x, y: infrontrightBlock.y, z: infrontrightBlock.z });
        let infrontnextRightBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: infrontnextRightBlock.x, y: infrontnextRightBlock.y, z: infrontnextRightBlock.z });
        let infrontnextLeftBlockPlace = world.getDimension(player.dimension.id).getBlock({ x: infrontnextLeftBlock.x, y: infrontnextLeftBlock.y, z: infrontnextLeftBlock.z });

        // Ensure no block is undefined
        if (!infrontleftBlockPlace || !infrontrightBlockPlace || !infrontnextRightBlockPlace || !infrontnextLeftBlockPlace) {
          // //////////world.sendMessage("One of the infront block placements is undefined.");
          return;
        }

        // Place all the infront blocks
        [OneBlockinfrontOfRoofPlace, infrontleftBlockPlace, infrontrightBlockPlace, infrontnextLeftBlockPlace, infrontnextRightBlockPlace].forEach((blockPos) => {
          player.runCommand("/fill " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " " + blockPos.x + " " + blockPos.y + " " + blockPos.z + " air destroy");
          // //////////world.sendMessage(blockPos.x + "" + blockPos.y + "" + blockPos.z + "" + blockPos.typeId);
          blockPos.setType("hb_cb:t2_v2_roof_2_block");
          blockPos.setPermutation(
            BlockPermutation.resolve("hb_cb:t2_v2_roof_2_block", {
              "minecraft:block_face": orientation,
            })
          );
        });
      }

      // let roof = world.getDimension(player.dimension.id).spawnEntity(item.typeId, placeLoc);
      let yRotation = getEntityRotation(player);

      // const playerYaw = player.getRotation().y * (Math.PI / 180); // Convert yaw from degrees to radians

      // // Get the tent's position (x, y, z)
      // const tentPos = tent.location;

      // // Calculate the position in front of the tent
      // const offsetX = Math.sin(playerYaw) * distance;
      // const offsetZ = -Math.cos(playerYaw) * distance;

      // // Calculate the new position (this will place the player in front of the tent)
      // const newPos = {
      //   x: tentPos.x + offsetX,
      //   y: tentPos.y, // Keep the same Y level as the tent (you can adjust this if needed)
      //   z: tentPos.z + offsetZ,
      // };

      player.runCommand("/summon " + item.typeId + " " + placeLoc.x.toString() + " " + (placeLoc.y + 2).toString() + " " + placeLoc.z.toString() + " " + yRotation.toString() + " 0");
      // player.runCommand("/setblock " + placeLoc.x.toString() + " " + (placeLoc.y + 2).toString() + " " + placeLoc.z.toString() + " diamond_block");
      if (!gamemodeCreative) {
        player.runCommand("/clear @s " + item.typeId + " 0 1");
      }
    }
  }

  despawnroof(roof) {
    roof.runCommand("/playanimation @s animation.hb_cb.roof.despawn");
    system.waitTicks(1).then(() => {
      let yRotationEntity = getEntityRotation(roof);
      let orientation;
      switch (yRotationEntity) {
        case 0:
          orientation = "north";
          break;
        case 90:
          orientation = "east";
          break;
        case 180:
          orientation = "south";
          break;
        case 270:
          orientation = "west";
          break;

        default:
          break;
      }

      if (roof.hasTag("hb_cb:start_block_despawn")) {
        roof.teleport({ x: roof.location.x, y: roof.location.y, z: roof.location.z });
      }

      let placeLoc = roof.location;
      const roofYaw = roof.getRotation().y * (Math.PI / 180); // Convert yaw from degrees to radians
      // Get the tent's position (x, y, z)
      const roofPos = {
        x: placeLoc.x,
        y: placeLoc.y - 2,
        z: placeLoc.z,
      };
      const oneBlocksoffsetX = Math.sin(roofYaw) * -1;
      const oneBlocksoffsetZ = -Math.cos(roofYaw) * -1;

      // Calculate the new position (this will place the player in front of the roof)
      const OneBlockinfrontOfRoof = {
        x: roofPos.x,
        y: roofPos.y, // Keep the same Y level as the roof (you can adjust this if needed)
        z: roofPos.z,
      };

      const twoBlocksoffsetX = Math.sin(roofYaw) * 1;
      const twoBlocksoffsetZ = -Math.cos(roofYaw) * 1;

      // Calculate the new position (this will place the player in front of the roof)
      const twoBlocksinfrontOfRoof = {
        x: roofPos.x + twoBlocksoffsetX,
        y: roofPos.y, // Keep the same Y level as the roof (you can adjust this if needed)
        z: roofPos.z + twoBlocksoffsetZ,
      };
      if (roof.typeId.includes("t1")) {
        let block = world.getDimension(roof.dimension.id).getBlock({ x: twoBlocksinfrontOfRoof.x, y: twoBlocksinfrontOfRoof.y + 2, z: twoBlocksinfrontOfRoof.z });
        let { leftBlock, rightBlock } = getSideBlockLocations(orientation, block);
        let leftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: leftBlock.x, y: leftBlock.y, z: leftBlock.z });
        let rightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: rightBlock.x, y: rightBlock.y, z: rightBlock.z });

        // Place all three blocks
        [block, leftBlockPlace, rightBlockPlace].forEach((blockPos) => {
          // //////////world.sendMessage(blockPos.x + "" + blockPos.y + "" + blockPos.y + "" + blockPos.typeId);
          blockPos.setType("air");
        });
        let infrontblock = world.getDimension(roof.dimension.id).getBlock({ x: OneBlockinfrontOfRoof.x, y: OneBlockinfrontOfRoof.y + 2, z: OneBlockinfrontOfRoof.z });
        const { leftBlock: leftBlock2, rightBlock: rightBlock2 } = getSideBlockLocations(orientation, infrontblock);
        leftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: leftBlock2.x, y: leftBlock2.y, z: leftBlock2.z });
        rightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: rightBlock2.x, y: rightBlock2.y, z: rightBlock2.z });

        // Place all three blocks
        [infrontblock, leftBlockPlace, rightBlockPlace].forEach((blockPos) => {
          // //////////world.sendMessage(blockPos.x + "" + blockPos.y + "" + blockPos.y + "" + blockPos.typeId);
          blockPos.setType("air");
        });
      }
      if (roof.typeId.includes("t2")) {
        let block = world.getDimension(roof.dimension.id).getBlock({ x: twoBlocksinfrontOfRoof.x, y: twoBlocksinfrontOfRoof.y + 2, z: twoBlocksinfrontOfRoof.z });

        let { leftBlock, rightBlock, nextLeftBlock, nextRightBlock } = getSideBlockLocations2Blocks(orientation, block);
        // //////////world.sendMessage("leftBlock: " + leftBlock + " rightBlock: " + rightBlock + " nextLeftBlock: " + nextLeftBlock + " nextRightBlock: " + nextRightBlock);

        // Validate block placements
        let leftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: leftBlock.x, y: leftBlock.y, z: leftBlock.z });
        let rightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: rightBlock.x, y: rightBlock.y, z: rightBlock.z });
        let nextRightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: nextRightBlock.x, y: nextRightBlock.y, z: nextRightBlock.z });
        let nextLeftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: nextLeftBlock.x, y: nextLeftBlock.y, z: nextLeftBlock.z });

        // Place all blocks
        [block, leftBlockPlace, rightBlockPlace, nextLeftBlockPlace, nextRightBlockPlace].forEach((blockPos) => {
          // //////////world.sendMessage(blockPos.x + "" + blockPos.y + "" + blockPos.z + "" + blockPos.typeId);
          blockPos.setType("air");
        });

        let OneBlockinfrontOfRoofPlace = world.getDimension(roof.dimension.id).getBlock({ x: OneBlockinfrontOfRoof.x, y: OneBlockinfrontOfRoof.y + 2, z: OneBlockinfrontOfRoof.z });

        let { leftBlock: infrontleftBlock, rightBlock: infrontrightBlock, nextLeftBlock: infrontnextLeftBlock, nextRightBlock: infrontnextRightBlock } = getSideBlockLocations2Blocks(orientation, OneBlockinfrontOfRoofPlace);

        // Validate infront block placements
        let infrontleftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: infrontleftBlock.x, y: infrontleftBlock.y, z: infrontleftBlock.z });
        let infrontrightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: infrontrightBlock.x, y: infrontrightBlock.y, z: infrontrightBlock.z });
        let infrontnextRightBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: infrontnextRightBlock.x, y: infrontnextRightBlock.y, z: infrontnextRightBlock.z });
        let infrontnextLeftBlockPlace = world.getDimension(roof.dimension.id).getBlock({ x: infrontnextLeftBlock.x, y: infrontnextLeftBlock.y, z: infrontnextLeftBlock.z });

        // Place all the infront blocks
        [OneBlockinfrontOfRoofPlace, infrontleftBlockPlace, infrontrightBlockPlace, infrontnextLeftBlockPlace, infrontnextRightBlockPlace].forEach((blockPos) => {
          blockPos.setType("air");
        });
      }

      let tier = stringBetween(roof.typeId, "hb_cb:", "_");
      let colorIndex = roof.getProperty("hb_cb:roof_color");
      let oldColorPropertyValue = roofColors[colorIndex];
      let spawnedItem = new ItemStack("hb_cb:" + tier + "_" + oldColorPropertyValue + "_roof");
      world.getDimension(roof.dimension.id).spawnItem(spawnedItem, roof.location);
      roof.remove();
    });
  }

  changeTentColor(player, roof, holdingItem) {
    let tier = stringBetween(roof.typeId, "hb_cb:", "_");
    let colorIndex = stringBetween(holdingItem.typeId, "minecraft:", "_dye");
    let newColorPropertyValue = getElementPosition(roofColors, colorIndex);
    let oldColorIndex = roof.getProperty("hb_cb:roof_color");
    let checkColorPropertyValue = roofColors[oldColorIndex];
    if (colorIndex == checkColorPropertyValue) return;
    else {
      let splashParticle = new MolangVariableMap();
      switch (tier) {
        case "t1":
          splashParticle.setFloat("variable.hb_cb_size", 0.8);
          splashParticle.setFloat("variable.hb_cb_radius", 1.5);
          splashParticle.setFloat("variable.hb_cb_y_offset", 1.5);
          break;
        case "t2":
          splashParticle.setFloat("variable.hb_cb_size", 1.2);
          splashParticle.setFloat("variable.hb_cb_radius", 2);
          splashParticle.setFloat("variable.hb_cb_y_offset", 1.7);
          break;
        default:
          break;
      }
      try {
        player.playSound("mob.squid.hurt", {
          location: player.location,
          pitch: 1.3,
          volume: 0.75,
        });
        roof.dimension.spawnParticle("hb_cb:color_splash_" + colorIndex, { x: roof.location.x, y: roof.location.y, z: roof.location.z }, splashParticle);
      } catch (error) {}

      roof.setProperty("hb_cb:roof_color", newColorPropertyValue);
      player.runCommand("/clear @s " + holdingItem.typeId + " 0 1");
    }
  }
}

function checkOldRoofDespawn(player) {
  removeOldRoofBlocks(player);
  let nearbyEntities = world.getDimension(player.dimension.id).getEntities({
    families: ["hb_cb_roof"],
    location: player.location,
    minDistance: 0,
    maxDistance: 5,
  });
  if (nearbyEntities.length === 0 || !nearbyEntities) return;
  nearbyEntities.forEach((roof) => {
    system.runTimeout(() => {
      if (!roof.isValid()) return;
      if (roof.hasTag("hb_cb:roof_spawned")) return;
      try {
        removeOldRoofBlocks(roof);
      } catch (error) {}
      let tier = stringBetween(roof.typeId, "hb_cb:", "_");
      let colorIndex = roof.getProperty("hb_cb:roof_color");
      let oldColorPropertyValue = roofColors[colorIndex];
      let spawnedItem = new ItemStack("hb_cb:" + tier + "_" + oldColorPropertyValue + "_roof");
      world.getDimension(roof.dimension.id).spawnItem(spawnedItem, roof.location);
      roof.remove();
    }, 20);
  });
}

function removeOldRoofBlocks(entity) {
  const volume = new BlockVolume({ x: entity.location.x + 3, y: entity.location.y + 2, z: entity.location.z + 3 }, { x: entity.location.x - 3, y: entity.location.y - 2, z: entity.location.z - 3 });

  const locations = volume.getBlockLocationIterator();

  for (const location of locations) {
    const block = entity.dimension.getBlock(location);
    if (!block) continue;
    const blockType = block.typeId;
    if (!roofBlocks.includes(blockType)) continue;
    block.setType("minecraft:air");
  }
}
