import BaseBoss from '../BaseBoss';
import { world, system, EntityDamageCause } from "@minecraft/server";
import { Knockback } from "../../utils/Knockback.js";
import { GetRandomInt } from "../../utils/GetRandomInt.js";
import * as utils from "../misc";
import { startCoroutineForBoss } from "../CoroutineClass";

const ABILITY_COOLDOWN_CONFIG = new Map([
  ["wave_attack", 2000], // Cooldown in milliseconds
  ["summon_attack", 10000] // Cooldown in milliseconds
]);

const TRY_ABILITY_COOLDOWN = 120; // 1000 ms between each check
class SeaSentryBoss extends BaseBoss {
  abilityCd;
  isSubscribed = false;

  constructor(entityId) {
    super('SeaSentryBoss', entityId);
    this.abilityCd = new Map();
  }

  async specialBehavior() {
    if (!this.isSubscribed) {
      world.afterEvents.dataDrivenEntityTrigger.subscribe((args) => {
        const { entity, eventId } = args;
        if (!this.entity.isValid()) return;
        if (entity.id === this.entityId) {
          switch (eventId) {
            case 'lv_mo:attack':
              this.basic_attack();
              break;
            case 'lv_mo:wave_attack':
              this.wave_attack();
              break;
            case 'lv_mo:summon_attack':
              this.summon_attack();
              break;
          }
        }
      });
      this.isSubscribed = true
    }
    this.tryActiveAbility();
  }

  async tryActiveAbility() {
    // Check each ability's cooldown status
    ABILITY_COOLDOWN_CONFIG.forEach((cooldownTime, abilityId) => {
      if (!this.entity.isValid()) return;
      if (this.isAbilityReady(abilityId)) {
        this.entity.removeTag(abilityId);
        this.abilityCd.set(abilityId, Date.now());  // Update cooldown timestamp after tag removal
      }
    });
  }

  // Helper function to check if an ability's cooldown has expired
  isAbilityReady(abilityId) {
    const now = Date.now();
    const lastUsed = this.abilityCd.get(abilityId) || 0;
    const cooldownTime = ABILITY_COOLDOWN_CONFIG.get(abilityId) || 0;
    //console.warn(abilityId, ' = ', (now - lastUsed), ' > ', cooldownTime)
    return (now - lastUsed) >= cooldownTime;
  }

  BASIC_ATTACK_DURATION = parseFloat(0.5 * 20)
  BASIC_ATTACK_TIME = parseFloat(0.13 * 20)
  BASIC_ATTACK_RADIUS = 3;
  BASIC_ATTACK_DAMAGE = 5;
  async basic_attack() {
    if (!this.entity.isValid()) return;
    this.entity.setProperty('lv_mo:animations', 'basic_attack');
    utils.facePlayer(this.entity, 1)
    if (!this.entity.isValid()) return;
    let victims = utils.getRadiusEntity(this.entity, utils.getPosForward(this.entity, 2), this.BASIC_ATTACK_RADIUS, ['player']);
    utils.delayExecute(this.BASIC_ATTACK_TIME, () => {
      if (!this.entity.isValid()) return;
      for (let victim of victims) {
        if (!this.entity.isValid()) return;
        victim.applyDamage(this.BASIC_ATTACK_DAMAGE, {
          cause: EntityDamageCause.entityAttack,
          damagingEntity: this.entity,
        });
        const knockbackMagnitude = GetRandomInt(2, 5);
        const knockback = Knockback.knockbackPosition(this.entity.location, victim.location, knockbackMagnitude);
        victim.applyKnockback(knockback.x, knockback.z, 2, 1.1);
      }
    });
    await this.wait((this.BASIC_ATTACK_DURATION - this.BASIC_ATTACK_TIME));

    // No cooldown set here, as basic_attack is always available
    await this.resetAndReadyAbility();
  }

  RANGE_ATTACK_PROJECTILE = 'lv_mo:sea_wave';
  RANGE_ATTACK_DURATION = parseFloat(2 * 20);
  RANGE_ATTACK_TIME = parseFloat(1.29 * 20);
  RANGE_ATTACK_TIP_DURATION = parseFloat(2 * 20);
  async wave_attack() {
    this.tipPlayer(15, 'avoid the wave attack');
    let i = 0;
    const test = system.runInterval(() => {
      if (!this.entity.isValid() || i >= this.RANGE_ATTACK_TIP_DURATION) {
        system.clearRun(test)
      }
      this.entity.runCommand("/execute as @s facing entity @p eyes run tp @s ~ ~ ~ ~ ~");
      i++
    }, 1)

    let active = function* () {
      if (!this.entity.isValid()) return;
      yield this.RANGE_ATTACK_TIP_DURATION;
      utils.facePlayer(this.entity, 1)
      this.entity.setProperty('lv_mo:animations', 'wave_attack');
      yield this.RANGE_ATTACK_TIME;
      if (!this.entity.isValid()) return;
      this.entity.runCommand(`scriptevent lm_vo:wave_attack`)
      const cooldownTime = ABILITY_COOLDOWN_CONFIG.get('wave_attack')
      yield (this.RANGE_ATTACK_DURATION - this.RANGE_ATTACK_TIME);
      if (!this.entity.isValid()) return;
      this.entity.addTag('wave_attack');
      this.abilityCd.set("wave_attack", Date.now() + 100 * this.RANGE_ATTACK_DURATION + cooldownTime);

    }.bind(this);
    startCoroutineForBoss(active, () => { this.resetAndReadyAbility(); }, this.entity.id);
  }

  SUMMON_ATTACK_ENTITY = 'minecraft:guardian';
  SUMMON_ATTACK_ENTITY_COUNT = 3;
  SUMMON_ATTACK_DURATION = 2.75 * 20;
  SUMMON_ATTACK_TIME = 2.25 * 20;
  SUMMON_ATTACK_TIP_DURATION = 2 * 20;

  async summon_attack() {
    this.tipPlayer(15, 'Sea Sentry will Summon some Guardians');
    let tickCounter = 0;

    const facingInterval = system.runInterval(() => {
      if (!this.entity.isValid() || tickCounter >= this.SUMMON_ATTACK_TIP_DURATION) {
        system.clearRun(facingInterval);
      } else {
        this.entity.runCommand("/execute as @s facing entity @p eyes run tp @s ~ ~ ~ ~ ~");
        tickCounter++;
      }
    }, 1);

    const summonAttackCoroutine = function* () {
      if (!this.entity.isValid()) return;

      yield this.SUMMON_ATTACK_TIP_DURATION;
      if (!this.entity.isValid()) return;

      utils.facePlayer(this.entity, 1);
      this.entity.setProperty('lv_mo:animations', 'summon_attack');

      yield this.SUMMON_ATTACK_TIME;

      if (!this.entity.isValid()) return;
      // Spread guardians from left to right in front of the entity
      for (let i = 0; i < this.SUMMON_ATTACK_ENTITY_COUNT; i++) {
        const offset = (i - Math.floor(this.SUMMON_ATTACK_ENTITY_COUNT / 2)) * 2; // Spread left and right

        if (!this.entity.isValid()) return;
        this.entity.runCommand(`/execute as @s positioned ^${offset} ^0 ^3 run summon ${this.SUMMON_ATTACK_ENTITY} ~ ~1 ~`);
        // ^X ^Y ^Z: X is left/right, Z is forward/backward relative to entity facing direction
      }

      const cooldownTime = ABILITY_COOLDOWN_CONFIG.get('summon_attack');
      yield (this.SUMMON_ATTACK_DURATION - this.SUMMON_ATTACK_TIME);

      if (!this.entity.isValid()) return;
      this.entity.addTag('summon_attack');
      this.abilityCd.set("summon_attack", Date.now() + 100 * this.SUMMON_ATTACK_DURATION + cooldownTime);
    }.bind(this);

    startCoroutineForBoss(summonAttackCoroutine, () => { this.resetAndReadyAbility(); }, this.entity.id);
  }

}

export default SeaSentryBoss;
