import { MolangVariableMap, system, Player, world, Direction } from "@minecraft/server";
import { getObjective, getScore, getMainhand, playSound, rotateVector } from "../utils";
import Vector from "../Vector"
import Constants from "../constants";
const chestBlow = {
  "ra:rpg": {
    mul: 1.9,
    heightMul: 1,
    minLimbDist: 64,
    blowDist: 9,
    chestDist: 64,
    legDist: 64,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:citori_725": {
    mul: 1.5,
    heightMul: 1,
    minLimbDist: 3,
    blowDist: 4.5,
    chestDist: 7,
    legDist: 7,
    arm: {
      dist: 8,
      damage: 100,
    },
    leg: {
      dist: 8,
      damage: 100,
    },
  },
  "ra:remington_870": {
    mul: 1.5,
    heightMul: 1,
    minLimbDist: 3,
    blowDist: 4.5,
    chestDist: 7,
    legDist: 7,
    arm: {
      dist: 8,
      damage: 100,
    },
    leg: {
      dist: 8,
      damage: 100,
    },
  },
  "ra:awm": {
    mul: 2.25,
    heightMul: 1,
    minLimbDist: 64,
    blowDist: 6,
    chestDist: 64,
    legDist: 64,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:mini_gun": {
    mul: 0.9,
    heightMul: 1,
    minLimbDist: 48,
    blowDist: 8,
    chestDist: 48,
    legDist: 48,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:magnum": {
    mul: 0.85,
    heightMul: 1,
    minLimbDist: 7,
    blowDist: 3,
    chestDist: 10,
    legDist: 16,
    arm: {
      dist: 16,
      damage: 100,
    },
    leg: {
      dist: 16,
      damage: 100,
    },
  },
  "ra:magnum_midnight": {
    mul: 0.85,
    heightMul: 1,
    minLimbDist: 7,
    blowDist: 3,
    chestDist: 10,
    legDist: 16,
    arm: {
      dist: 16,
      damage: 100,
    },
    leg: {
      dist: 16,
      damage: 100,
    },
  },
  "ra:desert_eagle": {
    mul: 0.85,
    heightMul: 1,
    minLimbDist: 7,
    blowDist: 3,
    chestDist: 10,
    legDist: 16,
    arm: {
      dist: 16,
      damage: 100,
    },
    leg: {
      dist: 16,
      damage: 100,
    },
  },
  "ra:desert_eagle_golden": {
    mul: 0.85,
    heightMul: 1,
    minLimbDist: 7,
    blowDist: 3,
    chestDist: 10,
    legDist: 16,
    arm: {
      dist: 16,
      damage: 100,
    },
    leg: {
      dist: 16,
      damage: 100,
    },
  },
  "ra:desert_eagle_netherite": {
    mul: 0.85,
    heightMul: 1,
    minLimbDist: 16,
    blowDist: 3,
    chestDist: 16,
    legDist: 16,
    arm: {
      dist: 16,
      damage: 100,
    },
    leg: {
      dist: 16,
      damage: 100,
    },
  },
  "ra:p250": {
    arm: {
      dist: 12,
      damage: 10,
    },
    leg: {
      dist: 12,
      damage: 10,
    },
  },
  "ra:mp5": {
    arm: {
      dist: 12,
      damage: 35,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
  },
  "ra:ppsh_41": {
    arm: {
      dist: 12,
      damage: 50,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
    chest: {
      dist: 12,
      damage: 10,
    },
  },
  "ra:ppsh_41_golden": {
    arm: {
      dist: 12,
      damage: 50,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
    chest: {
      dist: 12,
      damage: 10,
    },
  },
  "ra:mp40": {
    arm: {
      dist: 12,
      damage: 50,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
    chest: {
      dist: 12,
      damage: 10,
    },
  },
  "ra:mp40_golden": {
    arm: {
      dist: 12,
      damage: 50,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
    chest: {
      dist: 12,
      damage: 30,
    },
  },
  "ra:mp40_netherite": {
    arm: {
      dist: 12,
      damage: 50,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
    chest: {
      dist: 12,
      damage: 30,
    },
  },
  "ra:p90": {
    arm: {
      dist: 12,
      damage: 35,
    },
    leg: {
      dist: 12,
      damage: 35,
    },
  },
  "ra:m4": {
    arm: {
      dist: 32,
      damage: 40,
    },
    leg: {
      dist: 32,
      damage: 40,
    },
  },
  "ra:ak_47": {
    arm: {
      dist: 32,
      damage: 50,
    },
    leg: {
      dist: 32,
      damage: 50,
    },
  },
  "ra:ak_47_golden": {
    arm: {
      dist: 32,
      damage: 50,
    },
    leg: {
      dist: 32,
      damage: 50,
    },
  },
  "ra:ak_47_polymer": {
    arm: {
      dist: 32,
      damage: 50,
    },
    leg: {
      dist: 32,
      damage: 50,
    },
  },
  "ra:ak_47_polymer_golden": {
    arm: {
      dist: 32,
      damage: 50,
    },
    leg: {
      dist: 32,
      damage: 50,
    },
  },
  "ra:rpk": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 5,
    arm: {
      dist: 48,
      damage: 60,
    },
    leg: {
      dist: 48,
      damage: 60,
    },
  },
  "ra:rpk_polymer": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 5,
    arm: {
      dist: 48,
      damage: 60,
    },
    leg: {
      dist: 48,
      damage: 60,
    },
  },
  "ra:mx115": {
    mul: 1,
    heightMul: 1,
    blowDist: 4,
    chestDist: 48,
    legDist: 64,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:mx115_netherite": {
    mul: 1,
    heightMul: 1,
    blowDist: 4,
    chestDist: 48,
    legDist: 64,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:m1_garand": {
    arm: {
      dist: 32,
      damage: 50,
    },
    leg: {
      dist: 32,
      damage: 50,
    },
  },
  "ra:mosin_nagant": {
    arm: {
      dist: 56,
      damage: 50,
    },
    leg: {
      dist: 56,
      damage: 50,
    },
  },
  "ra:m16a4": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 4,
    arm: {
      dist: 48,
      damage: 50,
    },
    leg: {
      dist: 48,
      damage: 50,
    },
  },
  "ra:sp88": {
    mul: 1,
    heightMul: 1,
    blowDist: 4,
    chestDist: 48,
    legDist: 64,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:scar": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 2,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:scar_midnight": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 2,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
  "ra:scar_golden": {
    mul: 1,
    heightMul: 0,
    blowDist: 0,
    chestDist: 2,
    arm: {
      dist: 64,
      damage: 100,
    },
    leg: {
      dist: 64,
      damage: 100,
    },
  },
};
const lastHitObj = getObjective("ra_lasthit");
function detachLimb(entity, skinId, limbType, side) {
  if (entity.hasTag(`no_${limbType}.${side}`)) return;
  if (!side) side = Math.random() > 0.5 ? "r" : "l";
  entity.triggerEvent(`ra:remove_${limbType}.${side}`);
  const entLoc = entity.location;
  const isCrawler = entity.getComponent("is_stackable");
  let limbHeight = limbType == "arm" ? (isCrawler ? 0.5 : 1.5) : 0;
  let limbPos;
  if (side == "r") {
    limbPos = Vector.add(entLoc, { x: -0.25, y: limbHeight, z: 0 });
    entity.runCommand("execute at @s[tag=drop_loot] run summon minecraft:xp_orb ^-0.25^0.9^");
  } else {
    limbPos = Vector.add(entLoc, { x: 0.25, y: limbHeight, z: 0 });
    entity.runCommand("execute at @s[tag=drop_loot] run summon minecraft:xp_orb ^0.25^0.9^");
  }
  entity.addTag(`no_${limbType}.${side}`);
  const limb = entity.dimension.spawnEntity(`ra:zombie_limb<ra:skin_${skinId}>`, limbPos);
  limb.setRotation({ x: 0, y: Math.random() * 360 });
  limb.triggerEvent("ra:despawn_timer");
  const angle = side == "r" ? -135 : 135;
  limb.applyImpulse(
    Vector.add(Vector.multiply(rotateVector(entity.getViewDirection(), angle), 0.4), { x: 0, y: 0.05, z: 0 })
  );
}
function blowback(blowConf, dist, entity, skinId, hitVector, sideChar) {
  const entLoc = entity.location;
  const dim = entity.dimension;
  const isCrawler = entity.getComponent("minecraft:is_stackable");
  const chestHeight = isCrawler ? 0.185 : 1.4;
  const chestLoc = Vector.add(entLoc, { x: 0, y: chestHeight, z: 0 });
  const backLoc = Vector.subtract(chestLoc, Vector.multiply(entity.getViewDirection(), 0.5));
  if (dist <= blowConf.chestDist) {
    if (dist <= blowConf.blowDist) {
      const hitDir = Vector.add(Vector.multiply(hitVector, blowConf.mul * 0.4), {
        x: 0,
        y: 0.23 * blowConf.heightMul,
        z: 0,
      });
      const distMul = 1 - Math.pow(dist / blowConf.blowDist, 1.5);
      if (dist >= blowConf.minLimbDist) {
        const rlimb = Math.random();
        if (rlimb >= 0.5) detachLimb(entity, skinId, "arm", sideChar);
      }
      const yAng = (Math.atan2(hitVector.z, hitVector.x) * 180) / Math.PI + 90;
      entity.teleport(entLoc, { dimension: dim, rotation: { x: 0, y: yAng } });
      const blowback2 = dim.spawnEntity("ra:zombie_blowback", entLoc);
      entity.runCommand("ride @s start_riding @e[type=ra:zombie_blowback,c=1,tag=!init]");
      blowback2.addTag("init");
      blowback2.ownerId = entity.id;
      blowback2.applyImpulse(Vector.multiply(hitDir, distMul * 2.5));
      entity.triggerEvent("ra:blowback.fly");
    }
    const vars = new MolangVariableMap();
    dim.spawnParticle("ra:bullet.impact_zombie", chestLoc, vars);
    dim.spawnParticle("ra:blood", chestLoc, vars);
    dim.spawnParticle("ra:zombie_head.explode_blood", chestLoc, vars);
    dim.spawnParticle("ra:zombie_head.explode_blood_1", chestLoc, vars);
    dim.spawnParticle("ra:zombie_head.explode_blood", backLoc, vars);
    dim.spawnParticle("ra:zombie_head.explode_blood_1", backLoc, vars);
    entity.triggerEvent("ra:remove_chest");
    entity.kill();
  }
  playSound(dim, "splat_impact", { pitch: Math.random() * 0.75 + 0.75, volume: 3 }, { maxDistance: 60 }, entLoc);
}
const rotMap = {
  [Direction.East]: -90,
  [Direction.West]: 90,
  [Direction.North]: 180,
  [Direction.South]: 0,
  [Direction.Up]: void 0,
  [Direction.Down]: void 0,
};
world.afterEvents.projectileHitBlock.subscribe((ev) => {
  const proj = ev.projectile;
  try {
    if (proj.typeId != "ra:zombie_blowback") return;
  } catch {
    return;
  }
  const blockHit = ev.getBlockHit();
  if (!blockHit) return;
  const dim = ev.dimension;
  const zomb = proj.ownerId ? world.getEntity(proj.ownerId) : void 0;
  if (!zomb) return console.warn("no zombie");
  const { block, face } = blockHit;
  const rot = rotMap[face];
  let alignY = false;
  zomb.runCommandAsync("ride @s stop_riding").then(() => {
    let { x, y, z } = proj.location;
    if (alignY) y = Math.floor(y);
    zomb.teleport({ x, y, z }, { dimension: dim, rotation: { x: 0, y: rot ?? zomb.getRotation().y } });
    proj.triggerEvent("ra:despawn");
  });
  if (rot !== void 0) {
    const above = dim.getBlock(Vector.add(block, { x: 0, y: 1, z: 0 }));
    if (!above) return;
    const { x: bx, z: bz } = block;
    if (!above.isLiquid && !above.isAir) {
      zomb.triggerEvent("ra:blowback.wall");
      const vars = new MolangVariableMap();
      const dirVec = Constants.DIR_MAP[face];
      vars.setVector3("variable.block_face", dirVec);
      const partLoc = Vector.multiply(Vector.add(ev.location, Vector.multiply(dirVec, 0.01)), { x: 1, y: 0, z: 1 });
      let prevY = zomb.location.y + 0.02;
      let startTick = system.currentTick;
      const int = system.runInterval(() => {
        const newY = zomb.location.y;
        if (Math.abs(prevY - newY) < 5e-3 && system.currentTick - startTick > 5) {
          system.clearRun(int);
          return;
        }
        prevY = newY;
        const block2 = dim.getBlock({ x: bx, y: newY + 0.625, z: bz });
        if (!block2) return;
        if (!block2.isAir && !block2.isLiquid) {
          const newPartLoc = Vector.add(partLoc, {
            x: 0,
            y: newY + 0.75,
            z: 0,
          });
          dim.spawnParticle("ra:blood_splat.wall.3", newPartLoc, vars);
          dim.spawnParticle("ra:blood_splat.wall.3", Vector.add(newPartLoc, { x: 0, y: 0.4, z: 0 }), vars);
        }
      }, 1);
      system.runTimeout(() => system.clearRun(int), 20 * 2);
      const currPartLoc = Vector.add(partLoc, { x: 0, y: prevY + 1, z: 0 });
      dim.spawnParticle("ra:blood_splat.wall.2", currPartLoc, vars);
    } else {
      zomb.triggerEvent("ra:blowback.fold");
      zomb.triggerEvent("ra:no_gravity");
      alignY = true;
    }
  } else {
    const { x, y, z } = zomb.location;
    const viewDir = zomb.getViewDirection();
    const { x: vx, z: vz } = Vector.normalized(Vector.multiply(viewDir, {
      x: 1,
      y: 0,
      z: 1,
    }));
    const behind = dim.getBlock({
      x: Math.floor(x - vx),
      y,
      z: Math.floor(z - vz),
    });
    if (!behind) return;
    if (!behind.isAir && !behind.isLiquid) {
      zomb.triggerEvent("ra:blowback.floor_wall");
    } else {
      zomb.triggerEvent("ra:blowback.land");
    }
  }
});
world.afterEvents.projectileHitEntity.subscribe((ev) => {
  const src = ev.source;
  if (!(src instanceof Player)) return;
  const proj = ev.projectile;
  try {
    if (proj.typeId != "ra:bullet.projectile" && proj.typeId != "ra:rpg_rocket.projectile") return;
  } catch {
    return;
  }
  const entityHit = ev.getEntityHit();
  if (!entityHit) return;
  const entity = entityHit.entity;
  try {
    if (entity?.typeId !== "ra:zombie") return;
  } catch {
    return;
  }
  const ident = entity.scoreboardIdentity;
  const lastHit = ident ? getScore(lastHitObj, ident) : void 0;
  if (lastHit) {
    const dif = system.currentTick - lastHit;
    if (dif < 1) return;
  }
  entity.runCommand(`scoreboard players set @s ${lastHitObj.id} ${system.currentTick}`);
  const projLoc = ev.location;
  const entLoc = entity.location;
  const relLoc = Vector.subtract(projLoc, entLoc);
  const { y } = relLoc;
  const horPos = rotateVector(relLoc, entity.getRotation().y);
  const isRightHit = horPos.x < 0;
  const sideChar = isRightHit ? "r" : "l";
  const dim = ev.dimension;
  const gun = getMainhand(src);
  const gunId = gun?.typeId ?? "minecraft:air";
  const skinId = entity.getComponent("minecraft:skin_id")?.value ?? 0;
  const blowConf = chestBlow[gunId];
  const dist = Vector.length(Vector.subtract(entLoc, src.location));
  const isCrawler = entity.hasComponent("is_stackable");
  const isDead = entity.hasComponent("is_stunned");
  const isTnt = entity.hasComponent("is_charged");
  const isPumpkinHead = entity.hasComponent("is_ignited");
  const headHeight = isCrawler ? 0.71 : 1.5;
  const chestHeight = isCrawler ? 0.2 : 0.95;
  const maxChestHeight = isCrawler ? 0.7 : 1.6;
  const peckHeight = isCrawler ? 0.425 : 1.1;
  const maxPeckHeight = isCrawler ? 0.8 : 1.35;
  const shoulderLoc = Vector.add(entLoc, { x: 0, y: peckHeight, z: 0 });
  const headLoc = Vector.add(entLoc, { x: 0, y: headHeight, z: 0 });
  const hp2 = entity.damageLeg(sideChar, blowConf.arm.damage);

  if (y >= chestHeight && y < headHeight && isTnt && !isDead) {
    entity.triggerEvent("ra:tnt_explode");
  }

  if (isDead && isTnt && y >= 0.2) {
    entity.triggerEvent("ra:tnt_explode");
  }

  if (isDead && isPumpkinHead && y >= 0.2) {
    entity.triggerEvent("ra:tnt_explode");
  }

  if (y > headHeight) {
    if (entity.hasComponent("is_tamed")) {
      entity.runCommand("execute at @s[tag=drop_loot] run summon minecraft:xp_orb ~~1.5~");
      entity.runCommand("execute at @s[tag=drop_loot] run summon minecraft:xp_orb ~~1.5~");

      if (gunId == "ra:scar_golden" || gunId == "ra:ak_47_golden" || gunId == "ra:desert_eagle_golden" || gunId == "ra:mp40_golden" || gunId == "ra:ppsh_41_golden") {
        entity.runCommand('execute at @s[tag=drop_loot] run loot spawn ~~1.5~ loot "mobs/ra_zombie_gold_gun"');

        const vars = new MolangVariableMap();
        dim.spawnParticle("ra:gold.impact.burst.loot", headLoc, vars);
        dim.spawnParticle("ra:gold.impact.burst.loot1", headLoc, vars);
        dim.spawnParticle("ra:gold.impact.burst", headLoc, vars);

        entity.runCommand("playsound chime.amethyst_block @a ~~~ 1 0.65 0.01");
        entity.runCommand("playsound gold_loot @a[r=32] ~~~ 2 1.25 0.01");
        entity.runCommand("playsound gold_loot1 @a[r=32] ~~~ 2 1 0.01");
        entity.runCommand("playsound gold_loot2 @a[r=32] ~~~ 2 0.75 0.01");
        entity.runCommand("playsound gold_loot3 @a[r=32] ~~~ 2 2 0.01");
      }
      if (gunId == "ra:desert_eagle_netherite" || gunId == "ra:mp40_netherite" || gunId == "ra:mx115_netherite") {
        entity.runCommand('execute at @s[tag=drop_loot] run loot spawn ~~1.5~ loot "mobs/ra_zombie_netherite_gun"');

        const vars = new MolangVariableMap();
        dim.spawnParticle("ra:gold.impact.burst.loot", headLoc, vars);
        dim.spawnParticle("ra:gold.impact.burst.loot1", headLoc, vars);
        dim.spawnParticle("ra:gold.impact.burst", headLoc, vars);

        entity.runCommand("playsound chime.amethyst_block @a ~~~ 1 0.65 0.01");
        entity.runCommand("playsound gold_loot @a[r=32] ~~~ 2 1.25 0.01");
        entity.runCommand("playsound gold_loot1 @a[r=32] ~~~ 2 1 0.01");
        entity.runCommand("playsound gold_loot2 @a[r=32] ~~~ 2 0.75 0.01");
        entity.runCommand("playsound gold_loot3 @a[r=32] ~~~ 2 2 0.01");
      }

      const vars = new MolangVariableMap();
      dim.spawnParticle("ra:bullet.impact_zombie", headLoc, vars);
      dim.spawnParticle("ra:blood", headLoc, vars);
      dim.spawnParticle("ra:zombie_head.explode_blood", headLoc, vars);
      dim.spawnParticle("ra:zombie_head.explode_blood_1", headLoc, vars);
      playSound(dim, "splat_impact", { pitch: Math.random() * 0.75 + 1.25, volume: 3 }, { maxDistance: 60 }, entLoc);
      entity.triggerEvent("ra:remove_head");
      entity.triggerEvent("ra:death");

      entity.kill();
    }
  } else if (y < chestHeight) {
    if (blowConf.leg && dist <= blowConf.leg.dist && hp2 <= 0 && horPos.x >= -0.25 && horPos.x <= 0.25) {
      if (!entity.hasTag(`no_leg.${sideChar}`)) {
        detachLimb(entity, skinId, "leg", sideChar);
        playSound(dim, "splat_impact", { pitch: Math.random() * 0.75 + 1.75, volume: 3 }, { maxDistance: 60 }, entLoc);
        if (!isDead && !isCrawler) entity.triggerEvent("ra:zombie.crawler");
      }
    }
  } else {
    if (
      y <= maxPeckHeight &&
      y >= peckHeight &&
      blowConf.arm &&
      dist <= blowConf.arm.dist &&
      (horPos.x <= -0.175 || horPos.x >= 0.175) &&
      !isDead
    ) {
      const hp = entity.damageArm(sideChar, blowConf.arm.damage);
      if (hp <= 0 && !entity.hasTag(`no_arm.${sideChar}`)) {
        detachLimb(entity, skinId, "arm", sideChar);
        const vars = new MolangVariableMap();
        dim.spawnParticle("ra:bullet.impact_zombie", shoulderLoc, vars);
        dim.spawnParticle("ra:blood", shoulderLoc, vars);
        dim.spawnParticle("ra:zombie_head.explode_blood", shoulderLoc, vars);
        dim.spawnParticle("ra:zombie_head.explode_blood_1", shoulderLoc, vars);
        playSound(dim, "splat_impact", { pitch: Math.random() * 0.9 + 1.15, volume: 2 }, { maxDistance: 60 }, entLoc);
      }
    }
    if (blowConf && y <= maxChestHeight && "blowDist" in blowConf && horPos.x >= -0.175 && horPos.x <= 0.175) {
      blowback(blowConf, dist, entity, skinId, ev.hitVector, sideChar);
    }
    if (gunId == "ra:mini_gun" && !isDead) {
      const viewVec = entity.getViewDirection();
      detachLimb(entity, skinId, "arm", sideChar);
      if (Math.random() > 0.5) {
        entity.triggerEvent("ra:remove_head");
        const headOff = isCrawler ? 0.5 : 1.63;
        const head = dim.spawnEntity(
          `ra:zombie_head<ra:skin_${skinId}>`,
          Vector.add(entLoc, { x: 0, y: headOff, z: 0 })
        );
        head.triggerEvent("ra:despawn_timer");
        head.applyImpulse(Vector.add(Vector.multiply(viewVec, -0.25), { x: 0, y: 0.25, z: 0 }));
      }
      entity.triggerEvent("ra:death.mini_gun");
      entity.triggerEvent("ra:remove_chest");
      playSound(dim, "splat_impact", { pitch: Math.random() * 0.75 + 0.75, volume: 3 }, { maxDistance: 60 }, entLoc);
      entity.triggerEvent("ra:death");
      entity.kill();
    }
  }
});
