import { system, world, EquipmentSlot} from "@minecraft/server";
import { CombatAnimationParticle} from "./animations";
import { attackStates,comboStates, comboResetTasks ,playAnimationWithClock, getMoveDirection} from "./animation_controller";
import { setOrbitCamera, startCombat, stopCombat } from "./items";
import { DASH_DISTANCE, ENABLE_PLAYER_KNOCKBACK_RESISTANCE, ENABLE_ATTACK_PUSH, ENABLE_HIT_CHECK, ENABLE_SLASH_EFFECT, ATTACK_SLOWNESS, CombatAnimation, plantBlocks, isSword, isAxe, isShovel, isPickaxe, isPolearm, isGreatSword, isMace, isKatana, isDaggers, isRapier, isLongSword, isSaber } from "./combat_config";

// world.afterEvents.entityHitEntity.subscribe((event) => {
//   const attacker = event.damagingEntity;
//   const target = event.hitEntity;
//   if (target.typeId === "imc:entity_hit_check") return;
//   if (!attacker.getDynamicProperty("combat_state")) return;
//   handleAttack(attacker, undefined);
// });

// world.afterEvents.entityHitBlock.subscribe((event) => {
//   if (!event.damagingEntity.getDynamicProperty("combat_state")) return;
//   const player = event.damagingEntity;
//    // --- Get weapon type and corresponding radius & angle ---
//   const weaponType = getWeaponType(player.getComponent("minecraft:equippable")?.getEquipment(EquipmentSlot.Mainhand)?.typeId ?? "");
//   const comboInfo = CombatAnimation[weaponType];

//   if (player.getComponent("minecraft:equippable")?.getEquipment(EquipmentSlot.Mainhand)?.getComponent("minecraft:food")) return;

//   // fallback if somehow missing
//   const radius = comboInfo?.radius ?? 5;
//   const angle = comboInfo?.angle ?? 60;
//   // trigger swordSweep using dynamic radius & angle
//   swordSweep(player, radius, angle, 1, "entityAttack");
// });

// merge them all into one "all weapons" list
export const allWeaponKeywords = [
  ...isSword,
  ...isAxe,
  ...isShovel,
  ...isPickaxe,
  ...isPolearm,
  ...isGreatSword,
  ...isMace,
  ...isKatana,
  ...isDaggers,
  ...isRapier,
  ...isLongSword,
  ...isSaber
];

export function getWeaponType(typeId) {
  if (isMace.some(k => typeId.includes(k))) return "axe";
  else if (isPolearm.some(k => typeId.includes(k))) return "spear";
  else if (isGreatSword.some(k => typeId.includes(k))) return "greatsword";
  else if (isKatana.some(k => typeId.includes(k))) return "katana";
  else if (isDaggers.some(k => typeId.includes(k))) return "daggers";
  else if (isRapier.some(k => typeId.includes(k))) return "rapier";
  else if (isLongSword.some(k => typeId.includes(k))) return "long_sword";
  else if (isSaber.some(k => typeId.includes(k))) return "saber";
  else if (isSword.some(k => typeId.includes(k))) return "sword";
  else if (isAxe.some(k => typeId.includes(k))) return "axe";
  else if (isShovel.some(k => typeId.includes(k))) return "axe";
  else if (isPickaxe.some(k => typeId.includes(k))) return "axe";
  return "hand"; // fallback
}

export function isTooCloseToWall(player, maxDistance = 1) {
    const start = player.getHeadLocation();
    const dir = player.getViewDirection(); // full 3D direction (x,y,z)
    const dimension = player.dimension;

    if (dir.y < -0.5) { 
        return false; 
    }

    // normalize dir
    const len = Math.sqrt(dir.x * dir.x + dir.y * dir.y + dir.z * dir.z);
    if (len === 0) return false; // shouldn't happen
    const normDir = { x: dir.x / len, y: dir.y / len, z: dir.z / len };

    for (let d = 0; d <= maxDistance; d += 0.1) {
        const pos = {
            x: start.x + normDir.x * d,
            y: start.y + normDir.y * d,
            z: start.z + normDir.z * d
        };

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

        const blockId = block.typeId;
        if ([...plantBlocks].some(word => blockId.includes(word))) {
          continue;
        }

        // If block cannot be destroyed by liquids, it's solid
        if (!block.canBeDestroyedByLiquidSpread) {
            return true;
        }

        // Check for solid tags if available
        const blockTags = block.getTags ? block.getTags() : [];
        if (blockTags.some(tag => solidTags.has(tag))) {
            return true;
        }
    }

    return false; // no solid in ray
}

function getRayDirection(player, moveDir) {
    const dir = player.getViewDirection();
    const dlen = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
    if (dlen === 0) return { x: 0, y: 0, z: 0 };

    // Normalize view direction (XZ only)
    const ndx = dir.x / dlen;
    const ndz = dir.z / dlen;

    if (moveDir === "forward") {
        return { x: ndx, y: 0, z: ndz };
    }
    if (moveDir === "backward") {
        return { x: -ndx, y: 0, z: -ndz };
    }
    if (moveDir === "left") {
        return { x: -ndz, y: 0, z: ndx }; // rotate left
    }
    if (moveDir === "right") {
        return { x: ndz, y: 0, z: -ndx }; // rotate right
    }
    return { x: 0, y: 0, z: 0 };
}

export function shootMoveRayAndHit(player, maxDistance = 3) {
    const moveDir = getMoveDirection(player);
    if (!moveDir) return false; // not moving

    const rayDir = getRayDirection(player, moveDir);
    const start = player.getHeadLocation();
    const dimension = player.dimension;

    for (let d = 0; d <= maxDistance; d += 0.1) {
        const pos = {
            x: start.x + rayDir.x * d,
            y: start.y - 1.5, // check at feet
            z: start.z + rayDir.z * d
        };

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

        const blockId = block.typeId;
        if ([...plantBlocks].some(word => blockId.includes(word))) {
          continue;
        }

        if (!block.canBeDestroyedByLiquidSpread) {
            return true; // hit solid block
        }

        // Check for solid tags if available
        const blockTags = block.getTags ? block.getTags() : [];
        if (blockTags.some(tag => solidTags.has(tag))) {
            return true;
        }
    }

    return false; // no hit
}

export function swordSweep(player, radius, angle, baseDamage, cause = "sweep_attack") {
  const viewDir = player.getViewDirection(); // full 3D direction
  const origin = player.location;
  const halfAngle = (angle / 2) * (Math.PI / 180);

  const entities = player.dimension.getEntities({
    location: origin,
    maxDistance: radius,
  });

  const hitEntities = [];

  for (const e of entities) {
    if (e.id === player.id) continue;

    const headHeight = e.getHeadHeight?.() ?? 1;
    const positionsToCheck = [
      { // body center
        x: e.location.x,
        y: e.location.y + headHeight / 2,
        z: e.location.z
      },
      { // head
        x: e.location.x,
        y: e.location.y + headHeight,
        z: e.location.z
      }
    ];

    let insideCone = false;

    for (const pos of positionsToCheck) {
      const dx = pos.x - origin.x;
      const dy = pos.y - origin.y;
      const dz = pos.z - origin.z;

      const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
      if (len === 0) continue;

      // normalize direction to entity
      const normX = dx / len;
      const normY = dy / len;
      const normZ = dz / len;

      // normalize view direction
      const viewLen = Math.sqrt(viewDir.x * viewDir.x + viewDir.y * viewDir.y + viewDir.z * viewDir.z);
      if (viewLen === 0) continue;

      const viewX = viewDir.x / viewLen;
      const viewY = viewDir.y / viewLen;
      const viewZ = viewDir.z / viewLen;

      // dot product between normalized vectors
      const dot = viewX * normX + viewY * normY + viewZ * normZ;

      // angle between view direction and target direction
      const theta = Math.acos(Math.max(-1, Math.min(1, dot)));

      if (theta <= halfAngle) {
        insideCone = true;
        break; // no need to check other positions
      }
    }

    if (insideCone) {
      hitEntities.push(e);
    }
  }

  handleAttack(player, hitEntities, baseDamage, cause);
}
const solidTags = new Set([
    "acacia",
    "birch",
    "dark_oak",
    "diamond_pick_diggable",
    "dirt",
    "fertilize_area",
    "grass",
    "gravel",
    "iron_pick_diggable",
    "jungle",
    "log",
    "metal",
    "crop",
    "diamond_tier_destructible",
    "iron_tier_destructible",
    "is_axe_item_destructible",
    "is_hoe_item_destructible",
    "is_pickaxe_item_destructible",
    "is_shears_item_destructible",
    "is_shovel_item_destructible",
    "is_sword_item_destructible",
    "stone_tier_destructible",
    "mob_spawner",
    "not_feature_replaceable",
    "oak",
    "one_way_collidable",
    "plant",
    "pumpkin",
    "rail",
    "sand",
    "snow",
    "spruce",
    "stone",
    "stone_pick_diggable",
    "text_sign",
    "trapdoors",
    "water",
    "wood"
]);
// ----------------------
// Attack handler
// ----------------------
export function handleAttack(player, targets, baseDamage = 1, cause = "sweep_attack") {
  if (!player || player.typeId !== "minecraft:player") return;
  if (!player.getDynamicProperty("combat_state")) return;
  const dir = player.getViewDirection();
  let x = dir.x;
  let z = dir.z;

  const now = system.currentTick;
  const expiry = attackStates.get(player.id);

  let weaponType = "hand";
  const equippable = player.getComponent("minecraft:equippable");
  if (equippable) {
    const mainhand = equippable.getEquipment(EquipmentSlot.Mainhand);
    if (mainhand) {
      weaponType = getWeaponType(mainhand.typeId);
    }
  }

  const comboInfo = CombatAnimation[weaponType];
  const cooldown = comboInfo.cooldown;
  const slowdown = comboInfo.slowdown;
  const moveDistance = comboInfo.moveDistance;
  const comboVfxInfo = CombatAnimationParticle[weaponType];

  // If still in attack animation, apply weakness/slowness and return
  if (expiry && expiry > now) {
    player.addEffect("weakness", cooldown, { amplifier: 255, showParticles: true });
    player.addEffect("slowness", cooldown, { amplifier: slowdown, showParticles: true });
    return;
  }
  const distance = comboInfo.radius / 2;

  // Only handle attack if player is not too close to a wall
  if (isTooCloseToWall(player, distance)) {
    player.sendMessage("Sweep Attack Obstruted By Wall");
    return;
  }
  if (ENABLE_ATTACK_PUSH) {
    player.applyKnockback({ x: x * moveDistance, z: z * moveDistance }, 0);
  }
  // Apply damage to targets
  if (!Array.isArray(targets)) {
    targets = [targets]; // support single target
  }

  for (const target of targets) {
    if (!target || target.id === player.id) continue;
    if (!target.getComponent("minecraft:health")) continue;
    if (!target.getComponent("minecraft:type_family").hasTypeFamily("mob")) continue;
    // target.addEffect("weakness", cooldown, { amplifier: 255, showParticles: true });
    // target.clearVelocity();
    target.applyDamage(baseDamage, {
      cause,
      damagingEntity: player
    });
  }
  // const attack_volume = player.getDynamicProperty("attack_volume");
  // player.runCommand(`/playsound slash @a ~~~ ${attack_volume}`);

  // Trigger attack animation/combo logic
  attackTrigger(player, targets[0], now, slowdown, cooldown, comboInfo, weaponType, comboVfxInfo);
}


// ----------------------
// Attack trigger
// ----------------------
export function attackTrigger(player, target, now, slowdown, cooldown, comboInfo, weaponType, comboVfxInfo) {
  // console.warn(`Attack on ${target?.typeId}`);
  // Apply attack debuffs
  player.addEffect("weakness", cooldown, { amplifier: 255, showParticles: true });
  if (ATTACK_SLOWNESS) {
    player.addEffect("slowness", cooldown, { amplifier: slowdown, showParticles: true });
  }
  player.runCommand(
    `camerashake add @s ${comboInfo.shake_intensity} 0.2 positional`
  );
  const audio_cue = player.getDynamicProperty("audio_cue");

  player.runCommand(`title @s actionbar §c⋘▢⋙`);

  system.runTimeout(() => {
    player.runCommand(`title @s actionbar §2⋘▣⋙`);
    if (audio_cue !== "none"){
      const cue_volume = player.getDynamicProperty("cue_volume");
      player.runCommand(`/playsound ${audio_cue} @s ~~~ ${cue_volume}`);
    }
  }, comboInfo.cooldown);

  // Combo state
  let { weaponType: prevType, index } = comboStates.get(player.id) || {
    weaponType,
    index: 0,
  };

  if (prevType !== weaponType) index = 0;

  index++;

  if (weaponType !== "hand"){
    if (index === comboInfo.combo_stack) {
      player.addEffect("strength", 50, { amplifier: 1, showParticles: true });
    }

    let par = "combo_" + index;
    const particleName = comboVfxInfo[par]; // ✅ lookup string

    
    if (particleName && ENABLE_SLASH_EFFECT) {
      player.runCommand(`particle ${particleName} ^^^2`);
    }
  }

  if (index > comboInfo.combo_stack) {
    heavyAttack(player, comboInfo);
    comboStates.delete(player.id);
    return;
  }

  // Store combo state
  comboStates.set(player.id, { weaponType, index });

  // Set attack state expiry
  attackStates.set(player.id, now + comboInfo.cooldown);

  // Play sound
  playSound(player);
  const attack_volume = player.getDynamicProperty("attack_volume");
  player.runCommand(`/playsound ${comboInfo.sound} @a ~~~ ${attack_volume}`);

  // if (!target) {
  //   comboStates.delete(player.id);
  //   comboResetTasks.delete(player.id);
  //   return;
  // };
  // Reset combo after timeout
  if (comboResetTasks.has(player.id)) {
    system.clearRun(comboResetTasks.get(player.id));
  }
  const task = system.runTimeout(() => {
    comboStates.delete(player.id);
    comboResetTasks.delete(player.id);
  }, comboInfo.combo_frame);
  comboResetTasks.set(player.id, task);
}

export function heavyAttack(player, comboInfo) {
  // Fallback defaults
  const radius = comboInfo?.radius ?? 5;
  const halfAngle = (comboInfo?.angle / 2) * (Math.PI / 180);

  // Play heavy attack animation
  let anim = `${comboInfo.base}_heavy`;
  if (player.getDynamicProperty("camera_preset") === "dynamic_df") {
    anim =  anim + "_dnf";
  }
  playSound(player);
  playAnimationWithClock(player, anim, comboInfo.cooldown);

  const origin = player.location;
  const viewDir = player.getViewDirection(); // full 3D direction
  const dimension = player.dimension;

  // Normalize view direction (3D)
  const viewLen = Math.sqrt(viewDir.x * viewDir.x + viewDir.y * viewDir.y + viewDir.z * viewDir.z);
  if (viewLen === 0) return;
  const viewX = viewDir.x / viewLen;
  const viewY = viewDir.y / viewLen;
  const viewZ = viewDir.z / viewLen;

  const entities = dimension.getEntities({
    location: origin,
    maxDistance: radius,
  });

  for (const target of entities) {
    if (target.id === player.id) continue;

    // Aim at entity body center
    const headHeight = target.getHeadHeight?.() ?? 1;
    const centerY = target.location.y + headHeight / 2;

    const dx = target.location.x - origin.x;
    const dy = centerY - origin.y;
    const dz = target.location.z - origin.z;

    const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
    if (len === 0) continue;

    // Normalize direction to entity (3D)
    const normX = dx / len;
    const normY = dy / len;
    const normZ = dz / len;

    // Dot product between normalized vectors
    const dot = viewX * normX + viewY * normY + viewZ * normZ;

    // Angle between view direction and target direction
    const theta = Math.acos(Math.max(-1, Math.min(1, dot)));

    // Only apply to entities inside cone
    if (theta <= halfAngle) {
      const impulse = {
        x: normX * 0.5,
        y: 0.5,
        z: normZ * 0.5,
      };

      // Apply heavy attack debuffs
      target.addEffect("weakness", 100, { amplifier: 10, showParticles: true });
      target.addEffect("slowness", 60, { amplifier: 255, showParticles: true });
      target.addEffect("blindness", 100, { amplifier: 10, showParticles: true });

      // Knockback (skip for players to avoid abuse)
      if (target.typeId !== "minecraft:player") {
        target.applyImpulse(impulse);
      }
    }
  }

  // Sounds & camera shake
  const attack_volume = player.getDynamicProperty("attack_volume") ?? 1;
  player.runCommand(`/playsound ${comboInfo.sound}_heavy @a ~~~ ${attack_volume}`);
  player.runCommand(`camerashake add @s ${comboInfo.shake_intensity + 0.3} 0.3 positional`);

  // Reset combo & attack state
  comboStates.delete(player.id);
  attackStates.set(player.id, system.currentTick + comboInfo.cooldown);
}


export function dashPlayer(player, lifetime = 5, dash_type = "roll") {
    const dir = player.getViewDirection();
    player.runCommand(`/playsound dash @a ~~~`);
    player.addEffect("resistance", lifetime, { amplifier: 255, showParticles: true });
    if (Math.random() < 0.7) {
      playSound(player);
    }

    // Normalize horizontal direction
    let x = dir.x;
    let z = dir.z;
    const len = Math.sqrt(x * x + z * z);
    if (len > 0) {
        x /= len;
        z /= len;
    }

    // Add temporary tag to player
    const tag = "dash";
    player.addTag(tag);

    // Dash distance multiplier
    let distance = DASH_DISTANCE;

    switch (dash_type) {
        case "step_back":
            x = -x;
            z = -z;
            break;
        case "left":
            [x, z] = [-z, x];
            break;
        case "right":
            [x, z] = [z, -x];
            break;
        case "step_front":
        default:
            // forward dash
            break;
    }

    // Apply knockback (horizontal vector * distance, no vertical push)
    player.applyKnockback({ x: x * distance, z: z * distance }, 0);

    // Remove the player's tag after a short delay
    system.runTimeout(() => {
        player.removeTag(tag);
    }, lifetime);
}


const shieldList = ["shield", "buckler", "tower", "kite", "parma"]; // can add custom shields too
export function isHoldingWeapon(player) {
  const equippable = player.getComponent("minecraft:equippable");
  if (!equippable) return false;

  const mainhand = equippable.getEquipment(EquipmentSlot.Mainhand);
  const offhand = equippable.getEquipment(EquipmentSlot.Offhand);

  const isWeaponOrShield = (item) => {
    if (!item) return false;
    const typeId = item.typeId;
    return (
      allWeaponKeywords.some(word => typeId.includes(word)) ||
      shieldList.some(word => typeId.includes(word)) && allWeaponKeywords.some(word => typeId.includes(word))
    );
  };

  return isWeaponOrShield(mainhand) || isWeaponOrShield(offhand);
}

// Reset combo when the player gets hurt
world.afterEvents.entityHurt.subscribe((event) => {
  const player = event.hurtEntity;
  if (player.typeId !== "minecraft:player") return;
    // const dir = player.getViewDirection();
    // // Normalize horizontal direction
    // let x = dir.x;
    // let z = dir.z;
  if (ENABLE_PLAYER_KNOCKBACK_RESISTANCE) {
    player.applyKnockback({x: 0 , z: 0}, 0)
  }
});

// Remove dash tag whenever a player respawns or joins
world.afterEvents.playerSpawn.subscribe((event) => {
    const player = event.player;

    if (player.hasTag("dash")) {
        player.removeTag("dash");
    }

    if (player.getDynamicProperty('combat_state')){
      stopCombat(player);
      system.runTimeout(() => {
        if (ENABLE_HIT_CHECK) {
          startCombat(player);
        }
        setOrbitCamera(player);
      }, 20);
    }
    // player.setDynamicProperty("innit", false);

    const innit = player.getDynamicProperty("innit");

    if (!innit) {
        player.runCommand(`/give @p imc:combat_mode 1 0 {"keep_on_death": {}}`)
        player.setDynamicProperty("voice_type", "male");
        player.setDynamicProperty("voice_pitch", 1);
        player.setDynamicProperty("voice_volume", 0);
        player.setDynamicProperty("attack_volume", 1);
        player.setDynamicProperty("parry_volume", 1);
        player.setDynamicProperty("camera_vertical", 1);
        player.setDynamicProperty("camera_horizontal", 1.5);
        player.setDynamicProperty("camera_forward", -2);
        player.setDynamicProperty("camera_pitch", -10);
        player.setDynamicProperty("camera_yaw", -5);
        player.setDynamicProperty("camera_preset", "vanilla");
        player.setDynamicProperty("audio_cue", "none");
        player.setDynamicProperty("cue_volume", 1);
        player.setDynamicProperty("innit", true);


        // console.warn("=== Player dynamic properties set ===");
        // console.warn("voice_type: " + player.getDynamicProperty("voice_type"));
        // console.warn("voice_pitch: " + player.getDynamicProperty("voice_pitch"));
        // console.warn("voice_volume: " + player.getDynamicProperty("voice_volume"));
        // console.warn("attack_volume: " + player.getDynamicProperty("attack_volume"));
        // console.warn("parry_volume: " + player.getDynamicProperty("parry_volume"));
        // console.warn("camera_vertical: " + player.getDynamicProperty("camera_vertical"));
        // console.warn("camera_horizontal: " + player.getDynamicProperty("camera_horizontal"));
        // console.warn("camera_forward: " + player.getDynamicProperty("camera_forward"));
        // console.warn("camera_pitch: " + player.getDynamicProperty("camera_pitch"));
        // console.warn("camera_yaw: " + player.getDynamicProperty("camera_yaw"));
        // console.warn("camera_preset: " + player.getDynamicProperty("camera_preset"));
        // console.warn("innit: " + player.getDynamicProperty("innit"));
    }
});

// ----------------------
// Check if Player Holding Ranged Weapon
// ----------------------
export function isRangedWeapon(player) {
  const item = player.getComponent("minecraft:equippable").getEquipment(EquipmentSlot.Mainhand);
  if (!item) return false;

  // Keywords that define ranged weapons
  const rangedKeywords = [
    "bow",
    "crossbow",
    "rifle",
    "gun",
    "pistol",
    "shotgun",
    "sniper",
    "cannon",
    "wand"
  ];

  const id = item.typeId.toLowerCase();

  // Check if any keyword exists in item id
  return rangedKeywords.some(keyword => id.includes(keyword));
}

export function playSound(player, type = "normal"){
  const voice_type = player.getDynamicProperty("voice_type");
  const voice_pitch = player.getDynamicProperty("voice_pitch");
  const voice_volume = player.getDynamicProperty("voice_volume");
  if (type === "hit") {
    player.runCommand(`/playsound ${voice_type}_hit @a ~~~ ${voice_volume} ${voice_pitch}`);
  } else {
    player.runCommand(`/playsound ${voice_type} @a ~~~ ${voice_volume} ${voice_pitch}`);
  }
}