import { MolangVariableMap, TimeOfDay, system, world, ItemStack, StructureAnimationMode, StructureRotation, StructureSaveMode } from "@minecraft/server";
import { capitalizeFirst, getStringBeforeTent, checkLocation, getLocationbyFacingBlock, getEntityRotation, stringBetween, getGamemode, getElementPosition, getElementPositionArrayWithObjects } from "./helpers";
import { cameraCutscene } from "../events/camera";
import { ActionFormData, ModalFormData } from "@minecraft/server-ui";

const tentColors = ["white", "black", "blue", "brown", "cyan", "gray", "green", "light_blue", "light_gray", "lime", "magenta", "orange", "pink", "purple", "red", "yellow"];

const tentPatterns = [
  "no_pattern",
  "creeper_white",
  "creeper_black",
  "creeper_blue",
  "creeper_brown",
  "creeper_cyan",
  "creeper_gray",
  "creeper_green",
  "creeper_light_blue",
  "creeper_light_gray",
  "creeper_lime",
  "creeper_magenta",
  "creeper_orange",
  "creeper_pink",
  "creeper_purple",
  "creeper_red",
  "creeper_yellow",
  "stripped_white",
  "stripped_black",
  "stripped_blue",
  "stripped_brown",
  "stripped_cyan",
  "stripped_gray",
  "stripped_green",
  "stripped_light_blue",
  "stripped_light_gray",
  "stripped_lime",
  "stripped_magenta",
  "stripped_orange",
  "stripped_pink",
  "stripped_purple",
  "stripped_red",
  "stripped_yellow",
  "hearts_white",
  "hearts_black",
  "hearts_blue",
  "hearts_brown",
  "hearts_cyan",
  "hearts_gray",
  "hearts_green",
  "hearts_light_blue",
  "hearts_light_gray",
  "hearts_lime",
  "hearts_magenta",
  "hearts_orange",
  "hearts_pink",
  "hearts_purple",
  "hearts_red",
  "hearts_yellow",
  "waves_white",
  "waves_black",
  "waves_blue",
  "waves_brown",
  "waves_cyan",
  "waves_gray",
  "waves_green",
  "waves_light_blue",
  "waves_light_gray",
  "waves_lime",
  "waves_magenta",
  "waves_orange",
  "waves_pink",
  "waves_purple",
  "waves_red",
  "waves_yellow",
  "chequered_white",
  "chequered_black",
  "chequered_blue",
  "chequered_brown",
  "chequered_cyan",
  "chequered_gray",
  "chequered_green",
  "chequered_light_blue",
  "chequered_light_gray",
  "chequered_lime",
  "chequered_magenta",
  "chequered_orange",
  "chequered_pink",
  "chequered_purple",
  "chequered_red",
  "chequered_yellow",
  "cow_white",
  "cow_black",
  "cow_blue",
  "cow_brown",
  "cow_cyan",
  "cow_gray",
  "cow_green",
  "cow_light_blue",
  "cow_light_gray",
  "cow_lime",
  "cow_magenta",
  "cow_orange",
  "cow_pink",
  "cow_purple",
  "cow_red",
  "cow_yellow",
  "fire_white",
  "fire_black",
  "fire_blue",
  "fire_brown",
  "fire_cyan",
  "fire_gray",
  "fire_green",
  "fire_light_blue",
  "fire_light_gray",
  "fire_lime",
  "fire_magenta",
  "fire_orange",
  "fire_pink",
  "fire_purple",
  "fire_red",
  "fire_yellow",
  "lightning_white",
  "lightning_black",
  "lightning_blue",
  "lightning_brown",
  "lightning_cyan",
  "lightning_gray",
  "lightning_green",
  "lightning_light_blue",
  "lightning_light_gray",
  "lightning_lime",
  "lightning_magenta",
  "lightning_orange",
  "lightning_pink",
  "lightning_purple",
  "lightning_red",
  "lightning_yellow",
];

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

  initialize() {
    if (!tentHandler.initialized) {
      tentHandler.initialized = true;
      this.processSubscriptions();
    } else {
      //////////world.sendMessage("error, tent handler already initialized");
    }
  }
  processSubscriptions() {
    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("tent")) {
        const tent = event.target;
        if (!tent.getProperty("hb_cb:spawned")) return;
        const holdingItem = player.getComponent("equippable").getEquipment("Mainhand");
        if (holdingItem == undefined) {
          this.openUI(player, tent);
          return;
        }
        if (holdingItem.typeId.includes("minecraft:") && holdingItem.typeId.includes("dye")) {
          this.changeTentColor(player, tent, holdingItem);
        }
        if (holdingItem.typeId.includes("hb_cb:") && holdingItem.typeId.includes("pattern")) {
          // TODO: if pattern textures are done
          this.changeTentPattern(player, tent, holdingItem);
        }
        if (!(holdingItem.typeId.includes("minecraft:") && holdingItem.typeId.includes("dye")) && !(holdingItem.typeId.includes("hb_cb:") && holdingItem.typeId.includes("pattern"))) {
          this.openUI(player, tent);
        }
      } else return;
    });
    system.afterEvents.scriptEventReceive.subscribe((event) => {
      if (event.sourceEntity == undefined) return;
      else {
        if (event.sourceEntity.typeId.includes("hb_cb:") && event.sourceEntity.typeId.includes("tent")) {
          let tent = event.sourceEntity;
          switch (event.id) {
            case "hb_cb:despawn_tick_hit_trigger":
              if (!tent.getProperty("hb_cb:spawned")) return;
              if (tent.getProperty("hb_cb:despawn_tick") < 4) {
                //////////world.sendMessage("TENT HIT");
                let tier = stringBetween(tent.typeId, "_t", "_tent");
                tent.runCommand("/playanimation @s animation.hb_cb.t" + tier + "_tent.break");
                // hit_entity.runCommand("/playanimation @s animation.hb_cb." + hit_entity_name + "_light.hit");
                tent.runCommand("/event entity @s hb_cb:despawn_tick_hit");
              }
              break;
            case "hb_cb:tent_despawn":
              //////////world.sendMessage("DESPAWN TENT");
              this.despawnTent(tent);
              break;
            default:
              break;
          }
        }
      }
    });
    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("tent")) this.spawnTent(player, holdingItem);
    });
  }

  openUI(player, tent) {
    const tentForm = new ActionFormData();
    tentForm.title("§lTent Menu");
    tentForm.body("\nChoose a time to sleep until then\n\n");
    tentForm.button("Sunrise", "textures/hb/cb/items/sunrise");
    tentForm.button("Midday", "textures/hb/cb/items/midday");
    tentForm.button("Sunset", "textures/hb/cb/items/sunset");
    tentForm.button("Settings", "textures/hb/cb/items/settings");

    if (tent.hasTag("hb_cb:tent_spawnpoint")) player.runCommand("/spawnpoint @s ~ ~ ~ ");

    tentForm.show(player).then((data) => {
      switch (data.selection) {
        case 0:
          //////////world.sendMessage("Sunrise");

          player.runCommand("/playsound hb_cb:time_skip_morning_loop @a[r=10] ~~~ 1 1");
          this.timeShift(player, tent, 24000, "sunrise"); // Skips to the next sunrise
          break;
        case 1:
          //////////world.sendMessage("Midday");
          player.runCommand("/playsound hb_cb:skip_time_midday_loop @a[r=10] ~~~ 1 1");
          this.timeShift(player, tent, 6000, "midday"); // Skips to the next midday
          break;
        case 2:
          //////////world.sendMessage("Sunset");
          player.runCommand("/playsound hb_cb:time_skip_night_loop @a[r=10] ~~~ 1 1");
          this.timeShift(player, tent, 12000, "sunset"); // Skips to the next sunset
          break;
        case 3:
          const tentFormSpawnpoint = new ModalFormData();
          tentFormSpawnpoint.title("§lTent Settings");
          tentFormSpawnpoint.toggle("Set spawnpoint with sleeping", tent.hasTag("hb_cb:tent_spawnpoint") ? true : false);

          tentFormSpawnpoint.show(player).then((data) => {
            if (!data.formValues) return;
            const [spawnpoint] = data.formValues;
            if (spawnpoint) tent.addTag("hb_cb:tent_spawnpoint");
            else tent.removeTag("hb_cb:tent_spawnpoint");
          });
          break;
      }
    });
  }

  timeShift(player, tent, wanted_time, sleepTill) {
    let currentTime = world.getTimeOfDay();

    // Calculate the time difference
    let timeDifference;
    if (currentTime <= wanted_time) {
      timeDifference = wanted_time - currentTime;
    } else {
      // If the wanted time is in the next day, add 24000 to shift to the next occurrence
      timeDifference = 24000 - currentTime + wanted_time;
    }

    // Check if it's night (only allow skipping at night)
    // if (currentTime >= 12541 && currentTime <= 23999) {
    //////////world.sendMessage("Night detected. Starting time shift...");
    player.runCommand("/hud @s hide status_effects");
    //////////world.sendMessage("START time difference " + timeDifference.toString());
    //////////world.sendMessage("START currentTime " + currentTime.toString());
    //////////world.sendMessage("START wanted_time " + wanted_time.toString());
    player.teleport(tent.location, {
      rotation: tent.getRotation(),
    });

    player.runCommand("/ride @s start_riding @e[type=" + tent.typeId + ",c=1] teleport_rider");

    player.runCommand("/playsound hb_cb:time_skip_clock @a[r=10] ~~~ 1 1");
    // world.sendMessage("wait seconds: " + (timeDifference / 100 / 20).toString());
    cameraCutscene(player, tent, timeDifference);
    system.runTimeout(() => {
      this.stopTentSleep(player, tent, sleepTill);
    }, timeDifference / 100);

    // Recursive function to shift time incrementally
    const shiftTime = () => {
      if (timeDifference > 0) {
        let increment = Math.min(100, timeDifference);
        if (system.currentTick % 10 == 0) {
          tent.playAnimation("sleep");
        }
        system.waitTicks(1).then(() => {
          player.addEffect("resistance", 10, { amplifier: 254, showParticles: false });
          player.addTag("hb_cb:sleeping_in_tent");
          player.playAnimation("animation.hb_cb.player_sleep");
          player.runCommand(`/time add ${increment}`);
          timeDifference -= increment;

          // Update current time
          currentTime = world.getTimeOfDay();
          // //////////world.sendMessage("Current time: " + currentTime.toString());
          // //////////world.sendMessage("Time to shift: " + timeDifference.toString());

          // Continue shifting until we reach the target time
          if (timeDifference > 0) {
            shiftTime();
          } else {
            this.stopTentSleep(player, tent, sleepTill);
          }
        });
      }
    };

    // Start the time shifting process
    shiftTime();
    // } else {
    //   // Notify the player if it's not night
    //   //////////world.sendMessage("You can only use this command at night!");
    // }
  }

  stopTentSleep(player, tent, sleepTill) {
    player.runCommand("/hud @s reset status_effects");
    player.runCommand("/ride @s stop_riding");
    player.runCommand("/stopsound @a[r=10] hb_cb:time_skip_clock");
    this.teleportInFrontOfTent(player, tent, -2);
    try {
      player.dimension.spawnParticle("hb_cb:rested", player.getHeadLocation());
    } catch (error) {}
    player.runCommand("/inputpermission set @s camera enabled");
    player.runCommand("/inputpermission set @s movement enabled");
    let tier = stringBetween(tent.typeId, "_t", "_tent");
    switch (tier) {
      case "1":
        player.runCommand("/effect @s jump_boost 0");
        player.addEffect("jump_boost", 600, { amplifier: 1, showParticles: true });
        break;
      case "2":
        player.runCommand("/effect @s jump_boost 0");
        player.runCommand("/effect @s speed 0");
        player.addEffect("jump_boost", 600, { amplifier: 1, showParticles: true });
        player.addEffect("speed", 600, { amplifier: 1, showParticles: true });
        break;
      case "3":
        player.runCommand("/effect @s jump_boost 0");
        player.runCommand("/effect @s speed 0");
        player.runCommand("/effect @s strength 0");
        player.addEffect("jump_boost", 600, { amplifier: 1, showParticles: true });
        player.addEffect("speed", 600, { amplifier: 1, showParticles: true });
        player.addEffect("strength", 600, { amplifier: 1, showParticles: true });
        break;

      default:
        break;
    }
    switch (sleepTill) {
      case "sunrise":
        //////////world.sendMessage("Sunrise");
        player.runCommand("/playsound hb_cb:time_skip_morning_rooster @a[r=10] ~~~ 1 1");
        player.runCommand("/stopsound @a[r=10] hb_cb:time_skip_morning_loop");
        break;
      case "midday":
        //////////world.sendMessage("Midday");
        player.runCommand("/playsound hb_cb:skip_time_midday_dogs @a[r=10] ~~~ 1 1");
        player.runCommand("/stopsound @a[r=10] hb_cb:skip_time_midday_loop");
        break;
      case "sunset":
        //////////world.sendMessage("Sunset");
        player.runCommand("/playsound hb_cb:time_skip_night_dog_howling @a[r=10] ~~~ 1 1");
        player.runCommand("/stopsound @a[r=10] hb_cb:time_skip_night_loop");
        break;
    }
  }

  spawnTent(player, item) {
    //////////world.sendMessage("spawnTent");
    let tier = stringBetween(item.typeId, "_t", "_tent");
    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, 0)) return;

      // let diamond_poppy = world.getDimension(player.dimension.id).spawnEntity(item.typeId, placeLoc);
      let yRotation = getEntityRotation(player);
      player.runCommand("/summon " + item.typeId + " " + placeLoc.x.toString() + " " + placeLoc.y.toString() + " " + placeLoc.z.toString() + " " + yRotation.toString() + " 0");

      if (!gamemodeCreative) {
        player.runCommand("/clear @s " + item.typeId + " 0 1");
      }
      // let tent = world.getDimension(player.dimension.id).spawnEntity(item.typeId, placeLoc);
      // tent.teleport(tent.location, { rotation: { x: tent.getRotation().x, y: player.getRotation().y } });
      // player.runCommand("/summon " + item.typeId + " " + placeLoc.x.toString() + " " + placeLoc.y.toString() + " " + placeLoc.z.toString() + " facing " + player.location.x + " " + player.location.y + " " + player.location.z);
      let nearbyEntities = world.getDimension(player.dimension.id).getEntities({
        families: ["hb_cb_tent"],
        location: placeLoc,
        closest: 1,
      });
      if (nearbyEntities.length === 0 || nearbyEntities[0] == undefined) {
        return;
      } else {
        let pattern = item.getDynamicProperty("hb_cb:tent_pattern") ?? 0;

        //////////world.sendMessage("spawn pattern:" + pattern);
        //////////world.sendMessage("spawn patter222n:" + item.getDynamicProperty("hb_cb:tent_pattern"));
        // Old Pattern is NOT saved on item aka new tent
        if (pattern == 0) {
          //////////world.sendMessage("ITEM HAS NO PATTERN SAVED");
          nearbyEntities[0].setProperty("hb_cb:tent_pattern", 0);
          return;
        }
        // Old Pattern is saved on item aka old tent
        else {
          //////////world.sendMessage("ITEM HAS PATTERN SAVED");
          let patternIndex = pattern;
          let newPatternPropertyValue = tentPatterns[patternIndex].name;
          //////////world.sendMessage(newPatternPropertyValue.toString());
          nearbyEntities[0].setProperty("hb_cb:tent_pattern", patternIndex);
        }
      }
      if (!gamemodeCreative) {
        player.runCommand("/clear @s " + item.typeId + " 0 1");
      }
    }
  }

  despawnTent(tent) {
    let oldPatternPropertyValue;
    let lore;
    let tier = stringBetween(tent.typeId, "_t", "_tent");
    let colorIndex = tent.getProperty("hb_cb:tent_color");
    let oldColorPropertyValue = tentColors[colorIndex];
    let spawnedItem = new ItemStack("hb_cb:" + oldColorPropertyValue + "_t" + tier + "_tent");
    let patternIndex = tent.getProperty("hb_cb:tent_pattern") ?? 0;
    // //////////world.sendMessage(patternIndex.toString() + "pattern index");
    // //////////world.sendMessage(tent.getProperty("hb_cb:tent_pattern").toString() + "p22222attern index");
    if (patternIndex != 0) {
      let oldPatternIndex = tent.getProperty("hb_cb:tent_pattern") ?? 0;
      // world.sendMessage(oldPatternIndex.toString());
      let oldPatternPropertyValue = tentPatterns[oldPatternIndex];
      let key = capitalizeFirst(getStringBeforeTent("_", oldPatternPropertyValue));

      lore = ["Tent Pattern: " + key];
    } else {
      // //////////world.sendMessage("saved tent pattern");
      lore = false;
    }
    if (lore) {
      spawnedItem.setDynamicProperty("hb_cb:tent_pattern", patternIndex);
      spawnedItem.setLore(lore);
    }
    // //////////world.sendMessage("pattern:" + oldPatternPropertyValue);
    world.getDimension(tent.dimension.id).spawnItem(spawnedItem, tent.location);

    tent.runCommand("/playanimation @s animation.hb_cb.t" + tier + "_tent.despawn");
    system.waitTicks(1).then(() => {
      tent.remove();
    });
  }

  changeTentColor(player, tent, holdingItem) {
    let tier = stringBetween(tent.typeId, "_t", "_tent");
    let colorIndex = stringBetween(holdingItem.typeId, "minecraft:", "_dye");
    let newColorPropertyValue = getElementPosition(tentColors, colorIndex);
    let oldColorIndex = tent.getProperty("hb_cb:tent_color");
    let checkColorPropertyValue = tentColors[oldColorIndex];
    if (colorIndex == checkColorPropertyValue) return;
    else {
      //////////world.sendMessage(tier);
      let splashParticle = new MolangVariableMap();
      switch (tier) {
        case "1":
          splashParticle.setFloat("variable.hb_cb_size", 0.8);
          splashParticle.setFloat("variable.hb_cb_radius", 1.5);
          splashParticle.setFloat("variable.hb_cb_y_offset", 0.5);
          break;
        case "2":
          splashParticle.setFloat("variable.hb_cb_size", 1.2);
          splashParticle.setFloat("variable.hb_cb_radius", 2);
          splashParticle.setFloat("variable.hb_cb_y_offset", 1);
          break;
        case "3":
          splashParticle.setFloat("variable.hb_cb_size", 1.4);
          splashParticle.setFloat("variable.hb_cb_radius", 3);
          splashParticle.setFloat("variable.hb_cb_y_offset", 1.5);
          break;
        default:
          break;
      }
      try {
        player.playSound("mob.squid.hurt", {
          location: player.location,
          pitch: 1.3,
          volume: 0.75,
        });
        tent.dimension.spawnParticle("hb_cb:color_splash_" + colorIndex, { x: tent.location.x, y: tent.location.y, z: tent.location.z }, splashParticle);
      } catch (error) {}

      let oldPatternIndex = tent.getProperty("hb_cb:tent_pattern") ?? 0;
      // world.sendMessage(oldPatternIndex.toString());
      let oldPatternPropertyValue = tentPatterns[oldPatternIndex];
      // world.sendMessage("-1?;: " + oldPatternPropertyValue.toString());
      let key = oldPatternPropertyValue + "_" + colorIndex;
      // world.sendMessage("oldpatternpropterty: " + oldPatternPropertyValue.toString());
      oldPatternPropertyValue = getStringBeforeTent("_", oldPatternPropertyValue);
      // world.sendMessage("set to:  " + key.toString());

      let newPatternMatchingColor;
      if (oldPatternIndex == 0) {
        newPatternMatchingColor = "no_pattern";
      } else {
        newPatternMatchingColor = oldPatternPropertyValue + "_" + tentColors[newColorPropertyValue];
      }

      let newPatternMatchingColorValue = getElementPosition(tentPatterns, newPatternMatchingColor);

      tent.setProperty("hb_cb:tent_pattern", newPatternMatchingColorValue);

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

  // 2) Updated changeTentPattern with array lookup
  changeTentPattern(player, tent, holdingItem) {
    let tier = stringBetween(tent.typeId, "_t", "_tent");
    // world.sendMessage(tent.getProperty("hb_cb:tent_pattern").toString());
    // e.g. "hb_cb:blue_creeper_tent_pattern" → "blue_creeper"
    let oldColorIndex = tent.getProperty("hb_cb:tent_color");
    let checkColorPropertyValue = tentColors[oldColorIndex];
    let key = stringBetween(holdingItem.typeId, "hb_cb:", "_pattern") + "_" + checkColorPropertyValue;
    // world.sendMessage(key);
    let newPatternPropertyValue = tentPatterns.indexOf(key);
    if (holdingItem.typeId.includes("blank_pattern")) newPatternPropertyValue = 0;

    // world.sendMessage(newPatternPropertyValue.toString());
    if (newPatternPropertyValue < 0) {
      // unknown pattern → do nothing
      return;
    }

    let oldPatternIndex = tent.getProperty("hb_cb:tent_pattern");
    if (oldPatternIndex === newPatternPropertyValue) {
      // already set → do nothing
      return;
    }
    // world.sendMessage("hb_cb:color_splash_" + checkColorPropertyValue);
    let splashParticle = new MolangVariableMap();
    switch (tier) {
      case "1":
        splashParticle.setFloat("variable.hb_cb_size", 0.8);
        splashParticle.setFloat("variable.hb_cb_radius", 1.5);
        splashParticle.setFloat("variable.hb_cb_y_offset", 0.5);
        break;
      case "2":
        splashParticle.setFloat("variable.hb_cb_size", 1.2);
        splashParticle.setFloat("variable.hb_cb_radius", 2);
        splashParticle.setFloat("variable.hb_cb_y_offset", 1);
        break;
      case "3":
        splashParticle.setFloat("variable.hb_cb_size", 1.4);
        splashParticle.setFloat("variable.hb_cb_radius", 3);
        splashParticle.setFloat("variable.hb_cb_y_offset", 1.5);
        break;
      default:
        break;
    }
    try {
      player.playSound("mob.squid.hurt", {
        location: player.location,
        pitch: 1.3,
        volume: 0.75,
      });
      tent.dimension.spawnParticle("hb_cb:color_splash_" + checkColorPropertyValue, { x: tent.location.x, y: tent.location.y, z: tent.location.z }, splashParticle);
    } catch (error) {}
    // set property to new value and remove item from inventory
    tent.setProperty("hb_cb:tent_pattern", newPatternPropertyValue);

    let gamemodeCreative = getGamemode(player, 1);
    if (!gamemodeCreative) {
      player.runCommand(`/clear @s ${holdingItem.typeId} 0 1`);
    }
  }

  teleportInFrontOfTent(player, tent, distance) {
    // Get the player's current rotation (yaw)
    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,
    };

    // Teleport the player to the new position in front of the tent, keeping the same rotation
    player.tryTeleport(newPos, {
      checkForBlocks: true,
      rotation: tent.getRotation(),
    });
  }
}
