import { beforeEach, describe, expect, it } from "vitest";
import { BOSS_DEATH_DESPAWN_SECONDS } from "./bossDeath";
import { createClassInventory } from "./healers";
import { HOCKEY_HEALER_GOAL_Z, HOCKEY_MIDLINE_Z, HOCKEY_NPC_PADDLE_Z } from "./hockeyHealing";
import { useGameStore } from "./store";

describe("Hockey Healing encounter integration", () => {
  beforeEach(() => {
    useGameStore.getState().configureHealer(
      "priest",
      "Aelia",
      createClassInventory("priest"),
      ["bulldrome", "broodfang-spider"],
      "hockey-healing",
    );
  });

  it("starts two bosses on enemy half with healer defending own half", () => {
    const state = useGameStore.getState();
    expect(state.endlessMode).toBe(true);
    expect(state.additionalBosses).toHaveLength(1);
    expect(state.draftBuffIds).toEqual([]);
    expect(state.bossMotion.position[1]).toBeLessThan(HOCKEY_MIDLINE_Z);
    expect(state.additionalBosses[0].motion.position[1]).toBeLessThan(HOCKEY_MIDLINE_Z);
    expect(state.partyPositions.aelia[1]).toBeGreaterThan(HOCKEY_MIDLINE_Z);
    for (const memberId of ["brann", "nia", "orin", "vale"] as const) {
      expect(state.partyPositions[memberId][1]).toBeLessThan(HOCKEY_MIDLINE_Z);
    }
  });

  it("uses world-space left-stick direction when healer returns puck", () => {
    useGameStore.getState().startEncounter();
    useGameStore.getState().setHockeyAimDirection([1, -0.5]);
    useGameStore.setState((state) => ({
      hockey: { ...state.hockey, puckPosition: [0, 7.8], puckVelocity: [0, 5.2] },
      boss: { ...state.boss, nextMeleeAt: 999 },
      bossMotion: { ...state.bossMotion, nextMechanicAt: 999 },
      additionalBosses: state.additionalBosses.map((entry) => ({
        ...entry,
        boss: { ...entry.boss, nextMeleeAt: 999 },
        motion: { ...entry.motion, nextMechanicAt: 999 },
      })),
    }));

    useGameStore.getState().tick(0.2);

    expect(useGameStore.getState().hockey.returns).toBe(1);
    expect(useGameStore.getState().hockey.puckVelocity[0]).toBeGreaterThan(0);
    expect(useGameStore.getState().hockey.puckVelocity[1]).toBeLessThan(0);
  });

  it("ends run on wide-goal breach", () => {
    useGameStore.getState().startEncounter();
    useGameStore.getState().setPlayerPosition([7, 8]);
    useGameStore.setState((state) => ({
      hockey: { ...state.hockey, puckPosition: [0, HOCKEY_HEALER_GOAL_Z - 0.2], puckVelocity: [0, 5.2] },
    }));

    useGameStore.getState().tick(0.1);

    expect(useGameStore.getState().hockey.status).toBe("lost");
    expect(useGameStore.getState().phase).toBe("defeat");
  });

  it("keeps the rally going through the enemy Pong paddle", () => {
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      hockey: { ...state.hockey, puckPosition: [0, HOCKEY_NPC_PADDLE_Z + 1], puckVelocity: [0, -5.2] },
    }));

    useGameStore.getState().tick(0.2);

    const hockey = useGameStore.getState().hockey;
    expect(hockey.paddleReturns).toBe(1);
    expect(hockey.puckVelocity[1]).toBeGreaterThan(0);
    expect(useGameStore.getState().phase).toBe("combat");
  });

  it("spawns Soul Siphon only on the healer side", () => {
    useGameStore.getState().configureHealer(
      "priest",
      "Aelia",
      createClassInventory("priest"),
      ["mournveil-ghost", "bulldrome"],
      "hockey-healing",
    );
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      boss: { ...state.boss, nextMeleeAt: 999 },
      bossMotion: { ...state.bossMotion, mechanicCount: 2, nextMechanicAt: 0 },
      additionalBosses: state.additionalBosses.map((entry) => ({
        ...entry,
        boss: { ...entry.boss, nextMeleeAt: 999 },
        motion: { ...entry.motion, nextMechanicAt: 999 },
      })),
    }));

    useGameStore.getState().tick(0.1);

    const siphon = useGameStore.getState().bossMotion.poolTelegraphs[0].soulSiphon!;
    expect(siphon.wardPosition[1] - siphon.wardRadius).toBeGreaterThan(HOCKEY_MIDLINE_Z);
    expect(siphon.ghostPosition[1]).toBeGreaterThan(HOCKEY_MIDLINE_Z);
  });

  it("never marks the healer for Crushing Pounce", () => {
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      boss: { ...state.boss, nextMeleeAt: 999 },
      bossMotion: { ...state.bossMotion, mechanicCount: 9, nextMechanicAt: 0 },
      additionalBosses: state.additionalBosses.map((entry) => ({
        ...entry,
        boss: { ...entry.boss, nextMeleeAt: 999 },
        motion: { ...entry.motion, nextMechanicAt: 999 },
      })),
    }));

    useGameStore.getState().tick(0.05);

    const motion = useGameStore.getState().bossMotion;
    expect(motion.activeMechanicId).toBe("crushing-pounce");
    expect(motion.pounceTargetId).not.toBe("aelia");
  });

  it("replaces a fallen boss in same slot after death visual finishes", () => {
    useGameStore.getState().startEncounter();
    const defeatedInstanceId = useGameStore.getState().bossInstanceId;
    useGameStore.setState((state) => ({
      boss: { ...state.boss, hp: 0, defeatedAt: state.time },
      hockey: { ...state.hockey, puckVelocity: [0, 0] },
    }));

    let elapsed = 0;
    while (elapsed <= BOSS_DEATH_DESPAWN_SECONDS + 0.2) {
      useGameStore.getState().tick(0.1);
      elapsed += 0.1;
    }

    const replaced = useGameStore.getState();
    expect(replaced.phase).toBe("combat");
    expect(replaced.bossInstanceId).not.toBe(defeatedInstanceId);
    expect(replaced.bossInstanceId.startsWith("hockey-")).toBe(true);
    expect(replaced.boss.hp).toBe(replaced.boss.maxHp);
    expect(replaced.bossMotion.position[1]).toBeLessThan(HOCKEY_MIDLINE_Z);
    expect(replaced.additionalBosses).toHaveLength(1);
  });
});
