import { allMoves } from "#app/data/moves/move";
import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";

describe("Moves - Glaive Rush", () => {
  let phaserGame: Phaser.Game;
  let game: GameManager;

  beforeAll(() => {
    phaserGame = new Phaser.Game({
      type: Phaser.HEADLESS,
    });
  });

  afterEach(() => {
    game.phaseInterceptor.restoreOg();
  });

  beforeEach(() => {
    game = new GameManager(phaserGame);
    game.override
      .battleType("single")
      .disableCrits()
      .enemySpecies(Species.MAGIKARP)
      .enemyAbility(Abilities.BALL_FETCH)
      .enemyMoveset([Moves.GLAIVE_RUSH])
      .starterSpecies(Species.KLINK)
      .ability(Abilities.BALL_FETCH)
      .moveset([Moves.SHADOW_SNEAK, Moves.AVALANCHE, Moves.SPLASH, Moves.GLAIVE_RUSH]);
  });

  it("takes double damage from attacks", async () => {
    await game.classicMode.startBattle();

    const enemy = game.scene.getEnemyPokemon()!;
    enemy.hp = 1000;

    game.move.select(Moves.SHADOW_SNEAK);
    await game.phaseInterceptor.to("DamageAnimPhase");
    const damageDealt = 1000 - enemy.hp;
    await game.phaseInterceptor.to("TurnEndPhase");
    game.move.select(Moves.SHADOW_SNEAK);
    await game.phaseInterceptor.to("DamageAnimPhase");
    expect(enemy.hp).toBeLessThanOrEqual(1001 - damageDealt * 3);
  });

  it("always gets hit by attacks", async () => {
    await game.classicMode.startBattle();

    const enemy = game.scene.getEnemyPokemon()!;
    enemy.hp = 1000;

    allMoves[Moves.AVALANCHE].accuracy = 0;
    game.move.select(Moves.AVALANCHE);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(enemy.hp).toBeLessThan(1000);
  });

  it("interacts properly with multi-lens", async () => {
    game.override.startingHeldItems([{ name: "MULTI_LENS", count: 2 }]).enemyMoveset([Moves.AVALANCHE]);
    await game.classicMode.startBattle();

    const player = game.scene.getPlayerPokemon()!;
    const enemy = game.scene.getEnemyPokemon()!;

    enemy.hp = 1000;
    player.hp = 1000;

    allMoves[Moves.AVALANCHE].accuracy = 0;
    game.move.select(Moves.GLAIVE_RUSH);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBeLessThan(1000);
    player.hp = 1000;
    game.move.select(Moves.SPLASH);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBe(1000);
  });

  it("secondary effects only last until next move", async () => {
    game.override.enemyMoveset([Moves.SHADOW_SNEAK]);
    await game.classicMode.startBattle();

    const player = game.scene.getPlayerPokemon()!;
    const enemy = game.scene.getEnemyPokemon()!;

    enemy.hp = 1000;
    player.hp = 1000;
    allMoves[Moves.SHADOW_SNEAK].accuracy = 0;

    game.move.select(Moves.GLAIVE_RUSH);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBe(1000);

    game.move.select(Moves.SPLASH);
    await game.phaseInterceptor.to("TurnEndPhase");
    const damagedHp = player.hp;
    expect(player.hp).toBeLessThan(1000);

    game.move.select(Moves.SPLASH);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBe(damagedHp);
  });

  it("secondary effects are removed upon switching", async () => {
    game.override.enemyMoveset([Moves.SHADOW_SNEAK]).starterSpecies(0);
    await game.classicMode.startBattle([Species.KLINK, Species.FEEBAS]);

    const player = game.scene.getPlayerPokemon()!;
    const enemy = game.scene.getEnemyPokemon()!;

    enemy.hp = 1000;
    allMoves[Moves.SHADOW_SNEAK].accuracy = 0;

    game.move.select(Moves.GLAIVE_RUSH);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBe(player.getMaxHp());

    game.doSwitchPokemon(1);
    await game.phaseInterceptor.to("TurnEndPhase");
    game.doSwitchPokemon(1);
    await game.phaseInterceptor.to("TurnEndPhase");
    expect(player.hp).toBe(player.getMaxHp());
  });

  it("secondary effects don't activate if move fails", async () => {
    game.override.moveset([Moves.SHADOW_SNEAK, Moves.PROTECT, Moves.SPLASH, Moves.GLAIVE_RUSH]);
    await game.classicMode.startBattle();

    const player = game.scene.getPlayerPokemon()!;
    const enemy = game.scene.getEnemyPokemon()!;

    enemy.hp = 1000;
    player.hp = 1000;

    game.move.select(Moves.PROTECT);
    await game.phaseInterceptor.to("TurnEndPhase");

    game.move.select(Moves.SHADOW_SNEAK);
    await game.phaseInterceptor.to("TurnEndPhase");
    game.override.enemyMoveset([Moves.SPLASH]);
    const damagedHP1 = 1000 - enemy.hp;
    enemy.hp = 1000;

    game.move.select(Moves.SHADOW_SNEAK);
    await game.phaseInterceptor.to("TurnEndPhase");
    const damagedHP2 = 1000 - enemy.hp;

    expect(damagedHP2).toBeGreaterThanOrEqual(damagedHP1 * 2 - 1);
  });
});