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

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

  it("starts an endless two-boss rink with one five-brick row", () => {
    const state = useGameStore.getState();
    expect(state.endlessMode).toBe(true);
    expect(state.additionalBosses).toHaveLength(1);
    expect(state.blockbreaker.bricks).toHaveLength(5);
    expect(state.bossMotion.position[1]).toBeLessThan(HOCKEY_MIDLINE_Z);
    expect(state.partyPositions.aelia[1]).toBeGreaterThan(HOCKEY_MIDLINE_Z);
  });

  it("deals 25 partywide damage once and continues when a row breaches", () => {
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      boss: { ...state.boss, nextMeleeAt: 999 },
      additionalBosses: state.additionalBosses.map((entry) => ({
        ...entry,
        boss: { ...entry.boss, nextMeleeAt: 999 },
      })),
      blockbreaker: {
        ...state.blockbreaker,
        nextRowAt: state.time,
        puckVelocity: [0, 0],
        bricks: [{ id: "danger", row: BLOCKBREAKER_MAX_ROWS - 1, column: 2, color: "cyan" }],
      },
    }));
    expect(blockbreakerRowZ(BLOCKBREAKER_MAX_ROWS)).toBeGreaterThan(HOCKEY_MIDLINE_Z);

    useGameStore.getState().tick(0.1);
    const breached = useGameStore.getState();
    expect(breached.blockbreaker.status).toBe("live");
    expect(breached.blockbreaker.breaches).toBe(1);
    expect(breached.blockbreaker.bricks.some((brick) => brick.id === "danger")).toBe(false);
    expect(breached.party.every((member) => member.hp === member.maxHp - BLOCKBREAKER_BREACH_DAMAGE)).toBe(true);
    expect(breached.phase).toBe("combat");
    expect(breached.combatLog[0]?.message).toContain("25 damage to every party member");

    const healthAfterBreach = breached.party.map((member) => member.hp);
    useGameStore.getState().tick(0.1);
    expect(useGameStore.getState().party.map((member) => member.hp)).toEqual(healthAfterBreach);
  });

  it("misses safely re-serve without ending the run", () => {
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      blockbreaker: {
        ...state.blockbreaker,
        nextRowAt: 999,
        bricks: [],
        puckPosition: [0, HOCKEY_HEALER_GOAL_Z - 0.1],
        puckVelocity: [0, 6.25],
      },
    }));
    useGameStore.getState().tick(0.1);
    expect(useGameStore.getState().blockbreaker.status).toBe("reserving");
    expect(useGameStore.getState().phase).toBe("combat");
    useGameStore.getState().tick(1);
    expect(useGameStore.getState().blockbreaker.status).toBe("live");
    expect(useGameStore.getState().phase).toBe("combat");
  });

  it("fails when all four allies die even while the healer is alive", () => {
    useGameStore.getState().startEncounter();
    useGameStore.setState((state) => ({
      boss: { ...state.boss, nextMeleeAt: 999 },
      additionalBosses: state.additionalBosses.map((entry) => ({
        ...entry,
        boss: { ...entry.boss, nextMeleeAt: 999 },
      })),
      blockbreaker: { ...state.blockbreaker, nextRowAt: 999, puckVelocity: [0, 0] },
      party: state.party.map((member) => ({
        ...member,
        hp: member.id === "aelia" || member.id === "vale" ? member.maxHp : 0,
      })),
    }));

    useGameStore.getState().tick(0.1);
    expect(useGameStore.getState().phase).toBe("combat");

    useGameStore.setState((state) => ({
      party: state.party.map((member) => member.id === "vale" ? { ...member, hp: 0 } : member),
    }));
    useGameStore.getState().tick(0.1);

    const defeated = useGameStore.getState();
    expect(defeated.party.find((member) => member.id === "aelia")?.hp).toBeGreaterThan(0);
    expect(defeated.blockbreaker.status).toBe("lost");
    expect(defeated.blockbreaker.puckVelocity).toEqual([0, 0]);
    expect(defeated.phase).toBe("defeat");
    expect(defeated.combatLog[0]?.message).toContain("All four allies fell");
    const logLength = defeated.combatLog.length;
    useGameStore.getState().tick(0.1);
    expect(useGameStore.getState().combatLog).toHaveLength(logLength);
  });

  it("replaces fallen bosses and resets run state on retry", () => {
    useGameStore.getState().startEncounter();
    const defeatedInstanceId = useGameStore.getState().bossInstanceId;
    useGameStore.setState((state) => ({
      boss: { ...state.boss, hp: 1, nextMeleeAt: 999 },
      blockbreaker: { ...state.blockbreaker, nextRowAt: 999, puckVelocity: [0, 0], bricksBroken: 14, score: 720 },
    }));
    let elapsed = 0;
    while (useGameStore.getState().boss.hp > 0 && elapsed < 3) {
      useGameStore.getState().tick(0.1);
      elapsed += 0.1;
    }
    expect(useGameStore.getState().boss.hp).toBe(0);
    while (elapsed <= BOSS_DEATH_DESPAWN_SECONDS + 3.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("blockbreaker-")).toBe(true);
    expect(replaced.endlessBossKills).toBe(1);

    replaced.restart();
    const restarted = useGameStore.getState();
    expect(restarted.phase).toBe("briefing");
    expect(restarted.blockbreaker.bricksBroken).toBe(0);
    expect(restarted.blockbreaker.score).toBe(0);
    expect(restarted.blockbreaker.bricks).toHaveLength(5);
  });
});
