import { describe, expect, it } from "vitest";
import {
  HOCKEY_PVP_GOAL_DAMAGE,
  HOCKEY_PVP_GOAL_Z,
  advanceHockeyPvpPuck,
  createHockeyPvpState,
  hockeyPvpBossAt,
  hockeyPvpDampeningPercent,
  hockeyPvpHealingEffectiveness,
  hockeyPvpPuckSpeed,
  mirrorHockeyPvpPuck,
  reconcileHockeyPvpPuck,
} from "./hockeyHealingPvp";

describe("Healing Hockey PVP", () => {
  it("starts rallies at the faster default puck speed", () => {
    const state = createHockeyPvpState({ matchId: null, seed: 7, opponentName: "CPU", role: "cpu" });

    expect(hockeyPvpPuckSpeed(0)).toBe(7.8);
    expect(Math.hypot(...state.puckVelocity)).toBeCloseTo(7.8);
  });

  it("adds five percent global dampening for every boss killed by either party", () => {
    expect(hockeyPvpDampeningPercent(0, 0)).toBe(0);
    expect(hockeyPvpDampeningPercent(1, 0)).toBe(5);
    expect(hockeyPvpDampeningPercent(2, 3)).toBe(25);
    expect(hockeyPvpHealingEffectiveness(2, 3)).toBe(0.75);
    expect(hockeyPvpDampeningPercent(14, 9)).toBe(100);
    expect(hockeyPvpHealingEffectiveness(14, 9)).toBe(0);
  });

  it("uses same deterministic boss order for both parties", () => {
    const first = Array.from({ length: 20 }, (_, index) => hockeyPvpBossAt(4242, index));
    const second = Array.from({ length: 20 }, (_, index) => hockeyPvpBossAt(4242, index));
    expect(first).toEqual(second);
    expect(first.every(Boolean)).toBe(true);
    expect(new Set(first).size).toBeGreaterThan(1);
  });

  it("records a 45-damage goal signal and continues rally", () => {
    const source = createHockeyPvpState({ matchId: null, seed: 7, opponentName: "CPU", role: "cpu" });
    source.puckPosition = [0, HOCKEY_PVP_GOAL_Z - 0.1];
    source.puckVelocity = [0, 8];
    const result = advanceHockeyPvpPuck(source, {
      delta: 0.1,
      localPlayerPosition: [8.8, 8.5],
      localAimDirection: [0, -1],
      opponentPlayerPosition: [0, 8.5],
      opponentAimDirection: [0, -1],
    });

    expect(HOCKEY_PVP_GOAL_DAMAGE).toBe(45);
    expect(result.localGoalsConceded).toBe(1);
    expect(result.goalSequence).toBe(1);
    expect(result.status).toBe("live");
    expect(result.puckVelocity[1]).toBeGreaterThan(0);
  });

  it("mirrors authoritative puck state for guest perspective", () => {
    const source = createHockeyPvpState({ matchId: "match", seed: 9, opponentName: "Rival", role: "host" });
    source.puckPosition = [3, -8];
    source.puckVelocity = [-2, -6];
    source.localReturns = 4;
    source.opponentReturns = 7;
    source.localGoalsConceded = 2;
    source.opponentGoalsConceded = 1;
    source.goalSequence = 3;
    source.lastGoalSide = "opponent";

    expect(mirrorHockeyPvpPuck(source)).toMatchObject({
      puckPosition: [-3, 8],
      puckVelocity: [2, 6],
      localReturns: 7,
      opponentReturns: 4,
      localGoalsConceded: 1,
      opponentGoalsConceded: 2,
      lastGoalSide: "local",
    });
  });

  it("predicts guest movement between snapshots and soft-corrects small drift", () => {
    const guest = createHockeyPvpState({ matchId: "match", seed: 9, opponentName: "Rival", role: "guest" });
    guest.puckVelocity = [3, 6];
    const predicted = advanceHockeyPvpPuck(guest, {
      delta: 0.1,
      localPlayerPosition: [0, 8.5],
      localAimDirection: [0, -1],
      opponentPlayerPosition: [0, 8.5],
      opponentAimDirection: [0, -1],
    });
    expect(predicted.puckPosition[0]).toBeCloseTo(0.3);
    expect(predicted.puckPosition[1]).toBeCloseTo(0.6);
    expect(predicted.localGoalsConceded).toBe(0);

    const authoritative = { ...predicted, puckPosition: [0.6, 0.6] as [number, number] };
    const reconciled = reconcileHockeyPvpPuck(predicted, authoritative);
    expect(reconciled.puckPosition[0]).toBeGreaterThan(predicted.puckPosition[0]);
    expect(reconciled.puckPosition[0]).toBeLessThan(authoritative.puckPosition[0]);

    authoritative.goalSequence += 1;
    authoritative.puckPosition = [0, 0];
    expect(reconcileHockeyPvpPuck(predicted, authoritative).puckPosition).toEqual([0, 0]);
  });
});
