import { describe, expect, it } from "vitest";
import {
  AETHER_ARMORED_SCORE,
  AETHER_DIVE_DAMAGE,
  AETHER_ENEMY_SHOT_DAMAGE,
  AETHER_MAX_ENEMY_SHOTS,
  AETHER_MAX_PLAYER_SHOTS,
  AETHER_MAX_SHIPS,
  AETHER_STANDARD_SCORE,
  advanceAetherAssault,
  aetherMultiplier,
  aetherShipCount,
  aetherWaveClearBonus,
  createAetherAssaultState,
} from "./aetherAssault";

describe("Aether Assault", () => {
  it("creates deterministic eight-ship opening formations", () => {
    const first = createAetherAssaultState(true, 42);
    const second = createAetherAssaultState(true, 42);
    expect(first.ships).toEqual(second.ships);
    expect(first.ships).toHaveLength(8);
    expect(first.wave).toBe(1);
  });

  it("grows formations by two ships and caps them at twenty", () => {
    expect(aetherShipCount(1)).toBe(8);
    expect(aetherShipCount(4)).toBe(14);
    expect(aetherShipCount(99)).toBe(AETHER_MAX_SHIPS);
  });

  it("fires five fixed-forward player shots per second only while enabled", () => {
    let state = createAetherAssaultState(true, 7);
    state = advanceAetherAssault(state, {
      delta: 0.8,
      time: 0.8,
      playerPosition: [3, 8],
      canAutoFire: true,
    }).state;
    expect(state.playerShots.length).toBe(5);
    expect(state.playerShots.every((shot) => shot.velocity[0] === 0 && shot.velocity[1] < 0)).toBe(true);

    const paused = advanceAetherAssault(state, {
      delta: 1,
      time: 1.8,
      playerPosition: [3, 8],
      canAutoFire: false,
    }).state;
    expect(paused.playerShots.length).toBeLessThanOrEqual(state.playerShots.length);
  });

  it("awards kills, streak multiplier, armored points, and wave bonuses", () => {
    expect(aetherMultiplier(0)).toBe(1);
    expect(aetherMultiplier(5)).toBe(1.25);
    expect(aetherMultiplier(100)).toBe(5);
    expect(AETHER_STANDARD_SCORE).toBe(100);
    expect(AETHER_ARMORED_SCORE).toBe(250);
    expect(aetherWaveClearBonus(3)).toBe(2250);
  });

  it("applies the fifth-kill multiplier to an armored kill", () => {
    const state = createAetherAssaultState(true, 17);
    const ship = state.ships[0];
    ship.kind = "armored";
    ship.hp = 1;
    ship.maxHp = 3;
    ship.phase = "formation";
    ship.phaseStartedAt = 0;
    ship.position = [0, -5];
    ship.formationPosition = [0, -5];
    state.ships = [ship];
    state.killStreak = 4;
    state.multiplier = 1;
    state.playerShots = [{ id: 99, position: [0, 5], velocity: [0, -18] }];
    state.nextPlayerShotAt = 10;

    const next = advanceAetherAssault(state, {
      delta: 0.75,
      time: 1,
      playerPosition: [4, 8],
      canAutoFire: false,
    }).state;
    expect(next.killStreak).toBe(5);
    expect(next.multiplier).toBe(1.25);
    expect(next.lastKillScore).toBe(Math.round(AETHER_ARMORED_SCORE * 1.25));
    expect(next.score).toBe(next.lastKillScore + aetherWaveClearBonus(1));
  });

  it("uses segment collision for fast player projectiles", () => {
    const state = createAetherAssaultState(true, 9);
    const ship = state.ships[0];
    ship.phase = "formation";
    ship.phaseStartedAt = 0;
    ship.position = [0, -5];
    ship.formationPosition = [0, -5];
    state.ships = [ship];
    state.playerShots = [{ id: 99, position: [0, 5], velocity: [0, -18] }];
    state.nextPlayerShotAt = 10;

    const next = advanceAetherAssault(state, {
      delta: 0.75,
      time: 1,
      playerPosition: [4, 8],
      canAutoFire: false,
    }).state;
    expect(next.ships).toHaveLength(0);
    expect(next.kills).toBe(1);
    expect(next.score).toBe(AETHER_STANDARD_SCORE + aetherWaveClearBonus(1));
  });

  it("damages only through returned player damage and resets streak with hit grace", () => {
    const state = createAetherAssaultState(true, 3);
    state.killStreak = 8;
    state.multiplier = aetherMultiplier(8);
    state.enemyShots = [
      { id: 1, position: [0, 7], velocity: [0, 5] },
      { id: 2, position: [0.1, 7], velocity: [0, 5] },
    ];
    state.nextPlayerShotAt = 10;

    const hit = advanceAetherAssault(state, {
      delta: 0.4,
      time: 1,
      playerPosition: [0, 8],
      canAutoFire: false,
    });
    expect(hit.playerDamage).toBe(AETHER_ENEMY_SHOT_DAMAGE);
    expect(hit.state.killStreak).toBe(0);
    expect(hit.state.multiplier).toBe(1);
  });

  it("launches formation dives and resolves dive contact once", () => {
    const state = createAetherAssaultState(true, 23);
    const ship = state.ships[0];
    ship.phase = "formation";
    ship.phaseStartedAt = 0;
    ship.position = [0, -5];
    ship.formationPosition = [0, -5];
    state.ships = [ship];
    state.nextDiveAt = 0;
    state.nextEnemyShotAt = 99;
    state.nextPlayerShotAt = 99;

    const launched = advanceAetherAssault(state, {
      delta: 0.01,
      time: 0.01,
      playerPosition: [3, 8],
      canAutoFire: false,
    }).state;
    expect(launched.ships[0].phase).toBe("diving");
    expect(launched.ships[0].targetPosition[0]).toBe(3);

    launched.ships[0].position = [0, 0];
    launched.ships[0].startPosition = [0, 0];
    launched.ships[0].targetPosition = [0, 0];
    launched.ships[0].phaseStartedAt = 0;
    launched.nextDiveAt = 99;
    const contact = advanceAetherAssault(launched, {
      delta: 0.1,
      time: 0.1,
      playerPosition: [0, 0],
      canAutoFire: false,
    });
    expect(contact.playerDamage).toBe(AETHER_DIVE_DAMAGE);
    expect(contact.state.ships[0].contactResolved).toBe(true);
  });

  it("advances to a ten-ship second wave after the clear delay", () => {
    const state = createAetherAssaultState(true, 31);
    const ship = state.ships[0];
    ship.phase = "formation";
    ship.phaseStartedAt = 0;
    ship.position = [0, -5];
    ship.formationPosition = [0, -5];
    state.ships = [ship];
    state.playerShots = [{ id: 99, position: [0, 5], velocity: [0, -18] }];
    state.nextPlayerShotAt = 99;
    state.nextEnemyShotAt = 99;
    state.nextDiveAt = 99;

    const cleared = advanceAetherAssault(state, {
      delta: 0.75,
      time: 1,
      playerPosition: [4, 8],
      canAutoFire: false,
    }).state;
    expect(cleared.nextWaveAt).toBe(2.5);

    const next = advanceAetherAssault(cleared, {
      delta: 1.6,
      time: 2.6,
      playerPosition: [4, 8],
      canAutoFire: false,
    }).state;
    expect(next.wave).toBe(2);
    expect(next.ships).toHaveLength(10);
    expect(next.nextWaveAt).toBeNull();
  });

  it("never exceeds projectile caps", () => {
    const state = createAetherAssaultState(true, 12);
    state.nextPlayerShotAt = 0;
    state.nextEnemyShotAt = 0;
    state.playerShots = Array.from({ length: AETHER_MAX_PLAYER_SHOTS }, (_, id) => ({
      id,
      position: [9, 0] as [number, number],
      velocity: [0, -18] as [number, number],
    }));
    state.enemyShots = Array.from({ length: AETHER_MAX_ENEMY_SHOTS }, (_, id) => ({
      id: 100 + id,
      position: [9, -8] as [number, number],
      velocity: [0, 5] as [number, number],
    }));

    const next = advanceAetherAssault(state, {
      delta: 0.01,
      time: 0.01,
      playerPosition: [-9, 12],
      canAutoFire: true,
    }).state;
    expect(next.playerShots.length).toBeLessThanOrEqual(AETHER_MAX_PLAYER_SHOTS);
    expect(next.enemyShots.length).toBeLessThanOrEqual(AETHER_MAX_ENEMY_SHOTS);
  });
});
