import { world, system, ItemStack } from "@minecraft/server";
import { FoodMap } from "./foodMap.js";
import {
  SCOUT_SCOPE_LOOT_CONFIG,
  PROPHECY_SCOPE_LOOT_CONFIG,
} from "./scopeDropsList.js";

const teleportCooldowns = new Map();

const EXTRA_LOOT_CHANCE = 0.2; // 20% per tier

const tieredTrinketTags = [
  "pb_gat_trinket_boss_warden",
  "pb_gat_trinket_knockback",
  "pb_gat_trinket_heavy_dumbell",
  "pb_gat_trinket_iron_core",
  "pb_gat_trinket_teleport",
  "pb_gat_trinket_q_frog_leg",
  "pb_gat_trinket_thorns",
  "pb_gat_trinket_razor_cable",
  "pb_gat_trinket_comet_veil",
  "pb_gat_trinket_root",
  "pb_gat_trinket_scout",
  "pb_gat_trinket_lucky_scope",
  "pb_gat_trinket_relic_scope",
  "pb_gat_trinket_prophecy_scope",
  "pb_gat_trinket_loot",
  "pb_gat_trinket_wish_coin",
  "pb_gat_trinket_light_golden",
  "pb_gat_trinket_xp",
  "pb_gat_trinket_coin_amulet",
  "pb_gat_trinket_fortune_generator",
  "pb_gat_trinket_veteran_belt",
  "pb_gat_trinket_broken_blade",
  "pb_gat_trinket_food",
  "pb_gat_trinket_eat", // Placeholder until I can make auto eating work - need access to players hunger component
  "pb_gat_trinket_boss_dragon",
  "pb_gat_trinket_tiny_asteroid",
  "pb_gat_trinket_spectral_steel",
  "pb_gat_trinket_toxic_plasma",
  "pb_gat_trinket_draconic_conduit",
  "pb_gat_trinket_crystal_wings",
  "pb_gat_trinket_clockwork_wings",
  "pb_gat_trinket_typhoon_wings",
  "pb_gat_trinket_void_emitter",
  "pb_gat_trinket_light_lucky",
  "pb_gat_trinket_wings_time",
  "pb_gat_trinket_antimatter_wings",
];

// === On Item Use Complete ===
world.afterEvents.itemCompleteUse.subscribe((data) => {
  const player = data.source;
  const item = data.itemStack;
  if (player.typeId !== "minecraft:player" || !item) return;

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

  const tags = player.getTags();

  for (const baseTag of tieredTrinketTags) {
    for (const tag of tags) {
      if (tag.startsWith(baseTag + "_")) {
        const tierStr = tag.split("_").pop();
        const tier = parseInt(tierStr);
        if (!isNaN(tier)) {
          switch (baseTag) {
            case "pb_gat_trinket_food":
              handleFoodBonus(player, tier, foodInfo, itemId);
              break;
            case "pb_gat_trinket_eat":
              handleFoodBonus(player, tier + 1, foodInfo, itemId);
              break;
            case "pb_gat_trinket_crystal_wings":
              handleFoodBonus(player, tier * 2, foodInfo, itemId);
              break;
            case "pb_gat_trinket_clockwork_wings":
              handleFoodBonus(player, tier * 2, foodInfo, itemId);
              break;
            case "pb_gat_trinket_typhoon_wings":
              handleFoodBonus(player, tier * 2, foodInfo, itemId);
              break;
            case "pb_gat_trinket_wings_time":
              handleFoodBonus(player, tier * 2, foodInfo, itemId);
              break;
            case "pb_gat_trinket_antimatter_wings":
              handleFoodBonus(player, tier * 2, foodInfo, itemId);
              break;
          }
        }
      }
    }
  }
});
function handleFoodBonus(player, tier, foodInfo, itemId) {
  const extraFoodTicks = tier * (foodInfo.extraFood || 0);
  if (extraFoodTicks > 0) {
    player.addEffect("saturation", extraFoodTicks, {
      amplifier: 0,
      showParticles: false,
    });
  }
}
/// +++++ Hurt Events +++++ (thorns, TP, knockback, root)
world.afterEvents.entityHurt.subscribe((data) => {
  const victim = data.hurtEntity;
  const damage = data.damage;
  const attacker = data.damageSource?.damagingEntity;
  if (!attacker) return;

  // Who is the player?

  if (victim.typeId === "minecraft:player") {
    const player = victim;
    const tags = player.getTags();

    for (const baseTag of tieredTrinketTags) {
      for (const tag of tags) {
        if (tag.startsWith(baseTag + "_")) {
          const tierStr = tag.split("_").pop();
          const tier = parseInt(tierStr);
          if (!isNaN(tier)) {
            // Call function with tier
            switch (baseTag) {
              case "pb_gat_trinket_knockback":
                handleKnockbackResistance(player, tier);
                break;
              case "pb_gat_trinket_heavy_dumbell":
                handleKnockbackResistance(player, tier * 2);
                break;
              case "pb_gat_trinket_iron_core":
                handleKnockbackResistance(player, tier * 2);
                break;
              case "pb_gat_trinket_teleport":
                handleTeleport(player, tier);
                break;
              case "pb_gat_trinket_q_frog_leg":
                handleTeleport(player, tier);
                break;
              case "pb_gat_trinket_thorns":
                handleThorns(attacker, damage, tier);
                break;
              case "pb_gat_trinket_razor_cable":
                handleThorns(attacker, damage, tier * 2);
                break;
              case "pb_gat_trinket_tiny_asteroid":
                handleThorns(attacker, damage, tier * 2);
                handleKnockbackResistance(player, tier * 2);
                break;
              case "pb_gat_trinket_root":
                handleRoot(victim, tier);
                break;
              case "pb_gat_trinket_spectral_steel":
                handleKnockbackResistance(player, tier * 2);
                break;
              case "pb_gat_trinket_toxic_plasma":
                handleThorns(attacker, damage, tier * 2);
                break;
              case "pb_gat_trinket_draconic_conduit":
                handleTeleport(player, tier);
                break;
              case "pb_gat_trinket_clockwork_wings":
                handleTeleport(player, tier);
                break;
              case "pb_gat_trinket_void_emitter":
                handleThorns(attacker, damage, tier * 3);
                handleKnockbackResistance(player, tier * 3);
                break;
              case "pb_gat_trinket_wings_time":
                handleTeleport(player, tier);
                break;
              case "pb_gat_trinket_ruinous_blossom":
                handleThorns(attacker, damage, tier * 3);
                handleKnockbackResistance(player, tier * 3);
                break;
              case "pb_gat_trinket_antimatter_wings":
                handleTeleport(player, tier);
                break;
            }
          }
        }
      }
    }
  }

  if (attacker.typeId === "minecraft:player") {
    const player = attacker;
    const tags = player.getTags();
    if (data.damageSource.cause !== "entityAttack") return;
    for (const baseTag of tieredTrinketTags) {
      for (const tag of tags) {
        if (tag.startsWith(baseTag + "_")) {
          const tierStr = tag.split("_").pop();
          const tier = parseInt(tierStr);

          if (!isNaN(tier)) {
            // Call function with tier
            switch (baseTag) {
              case "pb_gat_trinket_boss_warden":
                handleKnockbackBoost(player, victim);
                break;
              case "pb_gat_trinket_root":
                handleRoot(victim, tier);
                break;
            }
          }
        }
      }
    }
  }
});

/// +++++ Die Events +++++ (loot, xp)
world.afterEvents.entityDie.subscribe((data) => {
  const victim = data.deadEntity;
  const attacker = data.damageSource?.damagingEntity;
  if (attacker?.typeId !== "minecraft:player") return;
  if (victim?.typeId === "minecraft:player") return;
  if (victim.typeId === "minecraft:wither") return; // Skip Wither here - uses sperate script instead

  const player = attacker;
  const tags = player.getTags();

  for (const baseTag of tieredTrinketTags) {
    for (const tag of tags) {
      if (tag.startsWith(baseTag + "_")) {
        const tierStr = tag.split("_").pop();
        const tier = parseInt(tierStr);

        switch (baseTag) {
          case "pb_gat_trinket_scout":
            handleScoutScope(victim, player, tier, false);
            break;
          case "pb_gat_trinket_lucky_scope":
            handleScoutScope(victim, player, tier * 2, false);
            break;
          case "pb_gat_trinket_relic_scope":
            handleScoutScope(victim, player, tier * 3, false);
            handleBrokenBlade(victim, player, tier);
            break;
          case "pb_gat_trinket_prophecy_scope":
            handleScoutScope(victim, player, tier * 3, true);
            handleBrokenBlade(victim, player, tier);
            break;
          case "pb_gat_trinket_loot":
            handleExtraLoot(victim, player, tier);
            break;
          case "pb_gat_trinket_coin_amulet":
            handleExtraLoot(victim, player, tier * 2);
            handleExtraXP(victim, tier * 2);
            break;
          case "pb_gat_trinket_light_golden":
            handleExtraLoot(victim, player, tier * 2);
            handleExtraXP(victim, tier * 2);
            break;
          case "pb_gat_trinket_fortune_generator":
            handleExtraLoot(victim, player, tier * 2);
            handleExtraXP(victim, tier * 2);
            break;
          case "pb_gat_trinket_wish_coin":
            handleExtraLoot(victim, player, tier * 2);
            break;
          case "pb_gat_trinket_xp":
            handleExtraXP(victim, tier);
            break;
          case "pb_gat_trinket_veteran_belt":
            handleExtraXP(victim, tier * 2);
            break;
          case "pb_gat_trinket_broken_blade":
            handleBrokenBlade(victim, player, tier);
            break;
          case "pb_gat_trinket_boss_dragon":
            handleBossDragon(victim);
            break;
          case "pb_gat_trinket_light_lucky":
            handleExtraLoot(victim, player, tier * 3);
            handleExtraXP(victim, tier * 3);
            break;
          case "pb_gat_trinket_light_supernova":
            handleExtraLoot(victim, player, tier * 3);
            handleExtraXP(victim, tier * 3);
            break;
        }
      }
    }
  }
});

export function handleScoutScope(
  victim,
  player,
  tier,
  prophecy = false,
  overrideLocation = null
) {
  const entityType = victim?.typeId; // fallback if victim is null

  const isSlime = entityType === "minecraft:slime";
  const isMagmaCube = entityType === "minecraft:magma_cube";

  if (isSlime || isMagmaCube) {
    const variant = victim.getComponent("minecraft:variant")?.value;
    if (variant !== 1) return; // Only small variant drops loot
  }

  function rollLoot(lootMap, label = "Standard") {
    const lootEntries = lootMap.get(entityType);
    if (!lootEntries) return;

    for (const entry of lootEntries) {
      const effectiveChance = Math.min(1, entry.baseChance * tier);

      const debugMessage = `[${label}] ${entry.item} → Base: ${Math.round(
        entry.baseChance * 100
      )}% x Tier: ${tier} = Final: ${Math.round(effectiveChance * 100)}%`;
      player.sendMessage(debugMessage);

      if (Math.random() < effectiveChance) {
        const itemToDrop = new ItemStack(entry.item, 1);

        const dropLocation = overrideLocation ?? victim?.location;
        if (!dropLocation) {
          return;
        }

        player.dimension.spawnItem(itemToDrop, dropLocation);
      }
    }
  }

  rollLoot(SCOUT_SCOPE_LOOT_CONFIG, "Standard");

  if (prophecy) {
    rollLoot(PROPHECY_SCOPE_LOOT_CONFIG, "Prophecy");
  }
}

function handleKnockbackResistance(player, tier) {
  // Define knockback reduction by tier
  // Tier 1: 80% knockback, Tier 2: 40%, Tier 3: 0%
  const reductionFactor = tier === 3 ? 0 : tier === 2 ? 0.4 : 0.8;

  for (let i = 0; i < 3; i++) {
    system.runTimeout(() => {
      const velocity = player.getVelocity();
      const horizontalSpeed = Math.sqrt(velocity.x ** 2 + velocity.z ** 2);

      if (horizontalSpeed > 0.01) {
        const reducedStrength = horizontalSpeed * reductionFactor;
        const nx = velocity.x / horizontalSpeed;
        const nz = velocity.z / horizontalSpeed;

        player.applyKnockback(nx, nz, reducedStrength, velocity.y * 0.2);
      }
    }, i);
  }
}
function handleTeleport(player, tier) {
  const playerId = player.id;
  const now = Date.now();

  // Tier-based cooldown (e.g. tier 1 = 15s, tier 2 = 10s, tier 3 = 5s)
  const cooldown = 15000 - (tier - 1) * 5000;

  const lastUsed = teleportCooldowns.get(playerId) ?? 0;

  // If player is still on cooldown, exit
  if (now - lastUsed < cooldown) return;

  const minDistance = 8;
  const maxDistance = 16;

  const { x, z } = player.location;

  // Run the teleport command asynchronously
  player
    .runCommandAsync(`spreadplayers ${x} ${z} ${minDistance} ${maxDistance} @s`)
    .then(() => {
      // Only set cooldown if command succeeds
      teleportCooldowns.set(playerId, now);
    });
}
function handleThorns(attacker, damage, tier) {
  const thornsDamage = damage * (0.33 * tier); // 33% per tier of the damage dealt to the player
  attacker.applyDamage(thornsDamage);
}
function handleRoot(victim, tier) {
  const rootDamage = tier;

  system.runTimeout(() => {
    victim.runCommandAsync(`health @s -${rootDamage}`);
  }, 10); // Delay by 10 ticks to allow attack immunity cooldown to expire
}
function handleBrokenBlade(victim, player) {
  const { dimension, location } = victim;
  system.runTimeout(() => {
    const radius = 3;

    const nearbyXP = dimension.getEntities({
      type: "minecraft:xp_orb",
      location,
      maxDistance: radius,
    });

    let removed = 0;
    for (const orb of nearbyXP) {
      orb.kill();
      removed++;
      if (removed >= 3) break;
    }
  }, 20); // 1 second delay
}
function handleExtraLoot(victim, player, tier) {
  if (victim.hasTag("pb_gat_fake_loot")) return;
  const chance = tier * EXTRA_LOOT_CHANCE;
  if (Math.random() > chance) return;

  const dim = player.dimension;
  const pos = {
    x: victim.location.x,
    y: victim.location.y,
    z: victim.location.z,
  };

  const typeId = victim.typeId;

  // Special case: Slimes & Magma Cubes
  const isSlime = typeId === "minecraft:slime";
  const isMagmaCube = typeId === "minecraft:magma_cube";

  if (isSlime || isMagmaCube) {
    const variant = victim.getComponent("minecraft:variant")?.value;
    if (variant !== 1) return; // Only small variant drops loot
  }

  const clone = dim.spawnEntity(typeId, pos);
  clone.addTag("pb_gat_fake_loot");
  clone.addEffect("invisibility", 200, {
    amplifier: 0,
    showParticles: false,
  });

  // Block XP drops for the duplicate
  const xp = clone.getComponent("minecraft:experience_reward");
  if (xp) xp.value = 0;

  // Copy sheep color if applicable
  const color = victim.getComponent("minecraft:color");
  if (color) {
    const cloneColor = clone.getComponent("minecraft:color");
    if (cloneColor) cloneColor.value = color.value;
  }

  // Apply 'spawn_small' event for slimes/magma cubes
  if (isSlime || isMagmaCube) {
    clone.triggerEvent("spawn_small");
  }

  // Damage the clone to make it drop loot
  system.runTimeout(() => {
    clone.applyDamage(1000, {
      cause: "entityAttack",
      damagingEntity: player,
    });
  }, 1);
}
function handleExtraXP(victim, tier) {
  if (victim.hasTag("pb_gat_fake_loot")) return;

  const dim = victim.dimension;
  const basePos = victim.location;
  const orbCount = getOrbCount(tier);
  for (let i = 0; i < orbCount; i++) {
    const offset = {
      x: (Math.random() - 0.5) * 0.5,
      y: Math.random() * 0.3,
      z: (Math.random() - 0.5) * 0.5,
    };

    dim.spawnEntity("minecraft:xp_orb", {
      x: basePos.x + offset.x,
      y: basePos.y + offset.y,
      z: basePos.z + offset.z,
    });
  }
}
function getOrbCount(tier) {
  tier = Number(tier) || 1; // fallback to 1 if tier is falsy or invalid
  const min = tier;
  const max = tier * 3;

  return Math.floor(Math.random() * (max - min + 1)) + min;
}
function handleKnockbackBoost(player, victim) {
  player.playSound("mob.warden.sonic_boom", { pitch: 1.0, volume: 1.0 });
  victim.dimension.spawnParticle("minecraft:sonic_explosion", {
    x: victim.location.x,
    y: victim.location.y + 0.5,
    z: victim.location.z,
  });
  victim.applyKnockback(
    victim.location.x - player.location.x,
    victim.location.z - player.location.z,
    3,
    0.5
  );
}
function handleBossDragon(victim) {
  if (victim.typeId != "minecraft:enderman") return;
  // 90% chance to drop an ender pearl
  if (Math.random() < 0.9) {
    const dimension = victim.dimension;
    const location = victim.location;
    dimension.spawnParticle("pb_gat:pearl_drop", {
      x: location.x,
      y: location.y + 0.5,
      z: location.z,
    });
    dimension.spawnItem(new ItemStack("minecraft:ender_pearl", 1), location);
  }
}
