import {
  system,
  world,
  BlockPermutation,
  ItemStack,
  MolangVariableMap,
} from "@minecraft/server";
import { highlightTeleportBlock } from "../blockHighlight.js";
import { highlightOre } from "../blockHighlightOre.js";
import { FoodMap } from "./foodMap.js";
import { pearlRayCastThrough } from "../nonSolidBlockLists.js";

const MAGNET_RANGE = 6; // Range for magnet to pull items - multiplied by tier
const DJVALUE = 0.7; // Double jump height

const lowHealthCooldowns = new Map(); // playerId => tick timestamp
const HEALTH_COOLDOWN_TICKS = 600; // 30 seconds * 20 ticks per second

const smelterLastUsed = new Map(); // Handle cooldown for smelting items in inventory - reduced based on tier
const SMELTABLES = new Map([
  // FOODS
  ["minecraft:beef", "minecraft:cooked_beef"],
  ["minecraft:chicken", "minecraft:cooked_chicken"],
  ["minecraft:fish", "minecraft:cooked_fish"],
  ["minecraft:muttonRaw", "minecraft:muttonCooked"],
  ["minecraft:rabbit", "minecraft:cooked_rabbit"],
  ["minecraft:porkchop", "minecraft:cooked_porkchop"],
  ["minecraft:salmon", "minecraft:cooked_salmon"],
  // ORES
  ["minecraft:ancient_debirs", "minecraft:netherite_scrap"],
  ["minecraft:raw_copper", "minecraft:copper_ingot"],
  ["minecraft:raw_gold", "minecraft:gold_ingot"],
  ["minecraft:raw_iron", "minecraft:iron_ingot"],
]);

const oreFinderCooldowns = new Map(); // Handle cooldown for pinging nearby ores - reduced based on tier
const ORE_LOCATOR_RANGE = 6;
const ORE_BLOCKS = new Map([
  ["minecraft:coal_ore", { r: 0.14, g: 0.14, b: 0.14 }],
  ["minecraft:copper_ore", { r: 0.95, g: 0.5, b: 0.4 }],
  ["minecraft:iron_ore", { r: 0.68, g: 0.55, b: 0.46 }],
  ["minecraft:gold_ore", { r: 0.92, g: 0.61, b: 0.05 }],
  ["minecraft:diamond_ore", { r: 0.11, g: 0.81, b: 0.83 }],
  ["minecraft:emerald_ore", { r: 0.09, g: 0.77, b: 0.26 }],
  ["minecraft:lapis_ore", { r: 0.09, g: 0.33, b: 0.74 }],
  ["minecraft:redstone_ore", { r: 0.79, g: 0.02, b: 0.02 }],
  ["minecraft:lit_redstone_ore", { r: 0.79, g: 0.02, b: 0.02 }],
  ["minecraft:deepslate_coal_ore", { r: 0.14, g: 0.14, b: 0.14 }],
  ["minecraft:deepslate_copper_ore", { r: 0.95, g: 0.5, b: 0.4 }],
  ["minecraft:deepslate_iron_ore", { r: 0.68, g: 0.55, b: 0.46 }],
  ["minecraft:deepslate_gold_ore", { r: 0.92, g: 0.61, b: 0.05 }],
  ["minecraft:deepslate_diamond_ore", { r: 0.11, g: 0.81, b: 0.83 }],
  ["minecraft:deepslate_emerald_ore", { r: 0.09, g: 0.77, b: 0.26 }],
  ["minecraft:deepslate_lapis_ore", { r: 0.09, g: 0.33, b: 0.74 }],
  ["minecraft:deepslate_redstone_ore", { r: 0.79, g: 0.02, b: 0.02 }],
  ["minecraft:lit_deepslate_redstone_ore", { r: 0.79, g: 0.02, b: 0.02 }],
  ["minecraft:quartz_ore", { r: 0.83, g: 0.79, b: 0.72 }],
  ["minecraft:nether_gold_ore", { r: 0.92, g: 0.61, b: 0.05 }],
  ["minecraft:ancient_debris", { r: 0.49, g: 0.37, b: 0.34 }],
  // add more ores with their color definitions here
]);

const trackedFallers = new Set();
const SAFE_FALL_SPEED = -0.55;

// Simplified effect function for this project
export function handleEffects(player, effect, tier) {
  player.addEffect(effect, 30, {
    amplifier: tier - 1, // Amplifier is 0-based
    showParticles: false,
  });
}
// Individual functions for effects that are not potions
export function handleDynamicLight(player, tier) {
  let block;
  try {
    block = player.dimension.getBlock(player.getHeadLocation());
  } catch {
    return;
  }

  if (!block || !block.isValid()) return;

  const isWater = block.typeId.includes("water") && block.isLiquid;
  const isReplaceable =
    block.isAir || isWater || block.typeId === "pb_gat:light_block";

  if (!isReplaceable) return;

  // Only set permutation if it's not already the correct one
  const currentPermutation = block.permutation;
  const currentTier = currentPermutation.getState("pb_gat:light_tier");

  if (block.typeId !== "pb_gat:light_block" || currentTier !== tier) {
    const newPermutation = BlockPermutation.resolve("pb_gat:light_block", {
      "pb_gat:light_tier": tier,
    });
    block.setPermutation(newPermutation);
  }
}
export function handleMagnetic(player, tier) {
  const dimension = player.dimension;
  const playerPos = player.location;
  const items = dimension.getEntities({
    location: playerPos,
    maxDistance: MAGNET_RANGE * tier,
    type: "minecraft:item",
  });
  for (const itemEntity of items) {
    const itemPos = itemEntity.location;
    const dx = playerPos.x - itemPos.x;
    const dy = playerPos.y - itemPos.y;
    const dz = playerPos.z - itemPos.z;
    const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
    if (dist < 0.1) continue;
    const step = 0.2;
    itemEntity.teleport({
      x: itemPos.x + (dx / dist) * step,
      y: itemPos.y + (dy / dist) * step,
      z: itemPos.z + (dz / dist) * step,
    });
  }
}
export function handleInvis(player, tier) {
  if (tier === 1) {
    const velocity = player.getVelocity();
    const isStill =
      Math.abs(velocity.x) < 0.01 &&
      Math.abs(velocity.y) < 0.01 &&
      Math.abs(velocity.z) < 0.01;

    if (isStill) {
      player.addEffect("invisibility", 3, {
        amplifier: tier - 1, // Amplifier is 0-based
        showParticles: false,
      });
    }
  } else {
    player.addEffect("invisibility", 10, {
      amplifier: tier - 1, // Amplifier is 0-based
      showParticles: false,
    });

    // Tier 3 - uses animation to make player completely invis - including armour and held items
    if (tier === 3) {
      player.playAnimation("animation.pb_gat.hide_player", {
        blendOutTime: 0,
        stopExpression: "query.any_animation_finished",
      });
    }
  }
}
export function handleFallDamage(player) {
  const vel = player.getVelocity();
  const pos = player.location;

  // Only proceed if falling
  if (vel.y >= 0 || player.isOnGround) {
    trackedFallers.delete(player.id);
    return;
  }

  trackedFallers.add(player.id);

  const dim = player.dimension;
  const fallSpeed = Math.abs(vel.y);

  const dynamicCheckRadius = Math.min(fallSpeed * 4, 12);
  const step = 0.5;

  for (let offset = step; offset <= dynamicCheckRadius; offset += step) {
    const blockPos = {
      x: Math.floor(pos.x),
      y: Math.floor(pos.y - offset),
      z: Math.floor(pos.z),
    };
    const blockBelow = dim.getBlock(blockPos);

    if (blockBelow && !blockBelow.isAir) {
      if (vel.y < SAFE_FALL_SPEED) {
        // Longer effect duration for safety
        player.addEffect("slow_falling", 2, { showParticles: false });
      }
      break;
    }
  }
}
export function handleDoubleJump(player) {
  if (player.canDoubleJump === undefined) player.canDoubleJump = true;
  if (player.jumpReleased === undefined) player.jumpReleased = true;

  const isOnGround = player.isOnGround;
  const isJumping = player.isJumping;
  const isInWater = player.isInWater;
  const isInAir = !isOnGround && !isInWater;

  // Reset ability to double jump if the player is on the ground or in water
  if (isOnGround || isInWater) {
    player.canDoubleJump = true;
    player.jumpReleased = false;
    player.removeTag("pb_gat.double_jump_triggered"); // Clear the tag if grounded again
  }

  if (isInAir && isJumping && player.canDoubleJump && player.jumpReleased) {
    player.canDoubleJump = false;
    player.jumpReleased = false;

    const DJVALUE = 0.6;

    if (player.isSprinting) {
      const view = player.getViewDirection();
      player.applyKnockback(view.x, view.z, 0.8, DJVALUE);
    } else {
      player.applyKnockback(0, 0, 0, DJVALUE);
    }

    // ✅ Tag the player to notify the glide system
    player.addTag("pb_gat.double_jump_triggered");
  }

  // Detect jump key release
  if (!isJumping && isInAir) {
    player.jumpReleased = true;
  }
}

export function handleLowHealth(player, tier) {
  const healthComp = player.getComponent("minecraft:health");
  const currentHealth = healthComp?.currentValue;
  if (currentHealth > 8) return;

  const now = system.currentTick;
  const playerId = player.id;

  const lastAppliedTick = lowHealthCooldowns.get(playerId) ?? -Infinity;

  if (now - lastAppliedTick < HEALTH_COOLDOWN_TICKS) {
    return; // Still on cooldown
  }

  // Apply the effect
  player.removeEffect("absorption");
  player.addEffect("absorption", 200, {
    amplifier: tier - 1,
    showParticles: false,
  });

  // Store the timestamp
  lowHealthCooldowns.set(playerId, now);
}
export function handleSmelter(player, tier) {
  const cooldown = 6000 - (tier - 1) * 2000;
  const playerId = player.id;
  const now = Date.now();

  // If first time, add player to map and return (delays first smelt)
  if (!smelterLastUsed.has(playerId)) {
    smelterLastUsed.set(playerId, now);
    return;
  }

  const lastUsed = smelterLastUsed.get(playerId);

  // Check if cooldown time has passed
  if (now - lastUsed < cooldown) return;

  const container = player.getComponent("inventory")?.container;
  if (!container) return;

  for (let i = 0; i <= 35; i++) {
    const item = container.getItem(i);
    if (!item) continue;

    const cookedId = SMELTABLES.get(item.typeId);
    if (!cookedId) continue;

    // Step 1: Try to find a partial stack of the cooked item
    let targetSlotIndex = -1;

    for (let j = 0; j <= 35; j++) {
      const slot = container.getItem(j);
      if (slot?.typeId === cookedId && slot.amount < 64) {
        targetSlotIndex = j;
        break;
      }
    }

    // Step 2: If no partial stack, look for an empty slot
    if (targetSlotIndex === -1) {
      for (let j = 0; j <= 35; j++) {
        const slot = container.getItem(j);
        if (!slot) {
          targetSlotIndex = j;
          break;
        }
      }
    }

    // Step 3: If still no room, cancel smelting
    if (targetSlotIndex === -1) {
      return;
    }

    // Consume 1 raw item
    if (item.amount > 1) {
      item.amount -= 1;
      container.setItem(i, item);
    } else {
      container.setItem(i, undefined);
    }

    // Place cooked item into chosen slot
    const targetItem = container.getItem(targetSlotIndex);
    if (targetItem) {
      targetItem.amount += 1;
      container.setItem(targetSlotIndex, targetItem);
    } else {
      container.setItem(targetSlotIndex, new ItemStack(cookedId, 1));
    }

    smelterLastUsed.set(playerId, now);

    break;
  }
}
export function handleAutoEat(player) {
  const hunger = player.getComponent("minecraft:player.hunger");
  if (hunger) {
  }
  if (!hunger) {
  }
  if (!hunger || hunger.foodLevel >= 20) return;
  const inventory = player.getComponent("minecraft:inventory");
  if (!inventory) return;

  const container = inventory.container;

  const tags = player.getTags();
  const foodTier = getFoodTrinketTier(tags);

  // Search inventory for first matching food item
  for (let i = 0; i < container.size; i++) {
    const item = container.getItem(i);
    if (!item) continue;

    const foodInfo = FoodMap.get(item.typeId);
    if (!foodInfo) continue;

    if (foodInfo.allowAutoEat === false) continue;

    // Remove 1 item
    if (item.amount > 1) {
      item.amount -= 1;
      container.setItem(i, item);
    } else {
      container.setItem(i, undefined);
    }

    // Apply saturation effect
    const baseTicks = foodInfo.defaultFood || 0;
    const bonusTicks = (foodInfo.extraFood || 0) * foodTier;
    const totalTicks = baseTicks + bonusTicks;

    if (totalTicks > 0) {
      player.addEffect("saturation", totalTicks, {
        amplifier: 0,
        showParticles: false,
      });
    }
    break; // Only eat one item
  }
}
function getFoodTrinketTier(tags) {
  for (const tag of tags) {
    if (tag.startsWith("pb_gat_trinket_food" + "_")) {
      const tierStr = tag.split("_").pop();
      const tier = parseInt(tierStr);
      if (!isNaN(tier)) {
        return tier;
      }
    }
  }
  return 0;
}
export function handleOreFinder(player, tier) {
  const COOLDOWN_MS = 5000; // 5 seconds
  const playerId = player.id;
  const now = Date.now();

  const lastUsed = oreFinderCooldowns.get(playerId) ?? 0;
  if (now - lastUsed < COOLDOWN_MS) return;

  oreFinderCooldowns.set(playerId, now); // Set cooldown

  const dimension = player.dimension;
  const playerPos = player.location;
  const radius = ORE_LOCATOR_RANGE * tier;

  const centerX = Math.floor(playerPos.x);
  const centerY = Math.floor(playerPos.y);
  const centerZ = Math.floor(playerPos.z);

  for (let x = -radius; x <= radius; x++) {
    for (let y = -radius; y <= radius; y++) {
      for (let z = -radius; z <= radius; z++) {
        const distanceSquared = x * x + y * y + z * z;
        if (distanceSquared > radius * radius) continue;

        const dx = centerX + x;
        const dy = centerY + y;
        const dz = centerZ + z;

        try {
          const block = dimension.getBlock({ x: dx, y: dy, z: dz });
          if (ORE_BLOCKS.has(block.typeId)) {
            revealOre(player, block);
          }
        } catch (error) {
          // Ignore invalid or unloaded blocks
        }
      }
    }
  }
}
// Helper for Ore Finder - creates a line of particles between player and ore block
function revealOre(player, block) {
  const start = player.location;
  const end = block.location;
  const steps = 8; // Number of particles in the line

  const dx = (end.x + 0.5 - start.x) / steps;
  const dy = (end.y - start.y) / steps;
  const dz = (end.z + 0.5 - start.z) / steps;

  const { r, g, b } = ORE_BLOCKS.get(block.typeId);
  const currentBlock = block.dimension.getBlock(block.location);
  system.runTimeout(() => {
    if (!currentBlock || currentBlock.typeId === "minecraft:air") return;

    highlightOre(currentBlock, getOreNameFromBlock(currentBlock));
  }, 10);

  for (let i = 1; i <= steps; i++) {
    const delayTicks = i * 2; // Delay per step (1 tick = 1/20th of a second)

    system.runTimeout(() => {
      const px = start.x + dx * i;
      const py = start.y + dy * i + 0.9;
      const pz = start.z + dz * i;

      let mvm = new MolangVariableMap();
      mvm.setFloat("variable.red", r);
      mvm.setFloat("variable.green", g);
      mvm.setFloat("variable.blue", b);

      player.dimension.spawnParticle(
        "pb_gat:ore_locator",
        { x: px, y: py, z: pz },
        mvm
      );
    }, delayTicks * 2);
  }
}
// Helper for Ore Finder - cleans up the block id so it can be used in the highlightBlock function
function getOreNameFromBlock(block) {
  let typeId = block.typeId; // e.g. "minecraft:lit_deepslate_redstone_ore"
  const name = typeId.split(":")[1]; // e.g. "lit_deepslate_redstone_ore"

  // Strip "lit_" prefix if present
  let cleaned = name.startsWith("lit_") ? name.substring("lit_".length) : name;

  // Strip "deepslate_" if present
  if (cleaned.startsWith("deepslate_")) {
    cleaned = cleaned.substring("deepslate_".length);
  }

  return cleaned; // e.g. "redstone_ore", "gold_ore", etc.
}
export function handleBossDragon(player) {
  const equippable = player.getComponent("equippable");
  const heldItem = equippable?.getEquipmentSlot("Mainhand")?.getItem();
  if (!heldItem || heldItem.typeId != "minecraft:ender_pearl") return;

  const headLoc = player.getHeadLocation();
  const viewDir = player.getViewDirection();

  const maxDistance = 50;
  const stepSize = 1;

  let foundBlock = false;

  for (let i = 0; i < maxDistance / stepSize; i++) {
    const testPos = {
      x: headLoc.x + viewDir.x * i * stepSize,
      y: headLoc.y + viewDir.y * i * stepSize,
      z: headLoc.z + viewDir.z * i * stepSize,
    };

    const blockLoc = {
      x: Math.floor(testPos.x),
      y: Math.floor(testPos.y),
      z: Math.floor(testPos.z),
    };

    const block = player.dimension.getBlock(blockLoc);
    if (!block) continue;

    if (!pearlRayCastThrough.includes(block.typeId)) {
      highlightTeleportBlock(block);

      player.setDynamicProperty(
        "pb_gat_target_tp_block",
        JSON.stringify(blockLoc)
      );

      foundBlock = true;
      break;
    }
  }

  // If no block found, clear the stored location
  if (!foundBlock) {
    player.setDynamicProperty("pb_gat_target_tp_block", undefined);
  }
}
const notTarget = [
  "minecraft:item",
  "minecraft:armor_stand",
  "minecraft:xp_orb",
  "minecraft:arrow",
];
export function witheredArrowBlock(data, projectile) {
  const owner = projectile.getComponent("projectile")?.owner;
  const entities = projectile.dimension.getEntities({
    location: {
      x: projectile.location.x,
      y: projectile.location.y,
      z: projectile.location.z,
    },
    maxDistance: 5,
  });
  entities.forEach((entity) => {
    //Ignore source player and not target array
    if (entity == owner) return;
    if (notTarget.includes(entity.typeId)) return;
    //Commands to run
    entity.addEffect("wither", 100, {
      amplifier: 0,
      showParticles: true,
    });
    entity.applyKnockback(
      projectile.location.x - entity.location.x,
      projectile.location.z - entity.location.z,
      2,
      1
    );
  });
  try {
    projectile.dimension.spawnEntity(
      "pb_gat:withered_arrow_dummy",
      projectile.location
    );
  } catch (error) {}
}
export function witheredArrowEntity(data, projectile, attacker) {
  const victim = data.getEntityHit().entity;
  const entities = victim.dimension.getEntities({
    location: {
      x: victim.location.x,
      y: victim.location.y,
      z: victim.location.z,
    },
    maxDistance: 5,
  });
  entities.forEach((entity) => {
    //Ignore source player and not target array
    if (entity == attacker) return;
    if (notTarget.includes(entity.typeId)) return;
    //Commands to run
    entity.addEffect("wither", 100, {
      amplifier: 0,
      showParticles: true,
    });
    entity.applyKnockback(
      entity.location.x - victim.location.x,
      entity.location.z - victim.location.z,
      2,
      1
    );
  });
  victim.dimension.spawnEntity("pb_gat:withered_arrow_dummy", victim.location);
}
