import { describe, expect, it } from "vitest";
import { ARENA_CENTER } from "./arena";
import { BULL_POUNCE } from "./bosses/mechanicPool";
import { createBaseMotion, createCircleHazard, returnBossToArenaCenter } from "./bosses/shared";
import { freshParty } from "./data";
import { distance, pointToSegmentDistance } from "./geometry";
import { combatFormation, updatePartyPositions } from "./partyBehaviors";
import type { MemberId, WorldPosition } from "./types";

describe("party boss positioning", () => {
  it("brings each boss back to its center slot instead of following an edge-displaced tank", () => {
    const motion = { ...createBaseMotion("bulldrome"), formationOffsetX: 2.65, position: [7, 5] as WorldPosition };

    expect(returnBossToArenaCenter(motion, 10, 2)).toBe(true);
    expect(motion.position).toEqual([ARENA_CENTER[0] + 2.65, ARENA_CENTER[1]]);
  });

  it("places Brann in front of the boss and Vale behind it", () => {
    const formation = combatFormation([2, -1]);

    expect(formation.brann).toEqual([2, 3.25]);
    expect(formation.vale).toEqual([2, -2.7]);
  });

  it("places drafted companions by combat kit instead of stable slot id", () => {
    const party = freshParty().map((member) => member.id === "brann"
      ? { ...member, runProfile: { instanceId: "ranged", combatProfileId: "warlock-damage", combatKitId: "ranged" as const, tier: "white", visualArchetype: "mage" as const } }
      : member.id === "nia"
        ? { ...member, runProfile: { instanceId: "tank", combatProfileId: "warrior-tank", combatKitId: "tank" as const, tier: "white", visualArchetype: "knight" as const } }
        : member);
    const formation = combatFormation([0, 0], party);

    expect(formation.brann[1]).toBe(7.2);
    expect(formation.nia[1]).toBe(4.25);
  });

  it("moves the tank toward the front and the rogue toward the rear during uptime", () => {
    const sharedStart: WorldPosition = [0, 0];
    const positions: Record<MemberId, WorldPosition> = {
      aelia: [0, 4],
      brann: [...sharedStart],
      nia: [-3, 4],
      orin: [3, 4],
      vale: [...sharedStart],
    };
    const motion = { ...createBaseMotion("bulldrome"), position: [0, -1] as WorldPosition };

    const next = updatePartyPositions(positions, motion, freshParty(), 1, 0.5);

    expect(next.brann[1]).toBeGreaterThan(sharedStart[1]);
    expect(next.vale[1]).toBeLessThan(sharedStart[1]);
  });

  it("keeps AI allies inside the enlarged pounce stack while avoiding another boss's danger", () => {
    expect(BULL_POUNCE.stackRadius).toBe(3.2);
    const initialStackCenter: WorldPosition = [0, 0];
    let positions: Record<MemberId, WorldPosition> = {
      aelia: [...initialStackCenter],
      brann: [...initialStackCenter],
      nia: [...initialStackCenter],
      orin: [...initialStackCenter],
      vale: [...initialStackCenter],
    };
    const dangerMotion = {
      ...createBaseMotion("crownshard-golem"),
      mode: "mantis_line_telegraph" as const,
      hazards: [createCircleHazard({
        id: "overlapping-impact",
        kind: "crownfall",
        center: initialStackCenter,
        radius: 1.2,
        activatesAt: 2,
        duration: 4,
        damage: 30,
      })],
      slashLanes: [{
        id: "overlapping-lane",
        start: [0, -5] as WorldPosition,
        end: [0, 5] as WorldPosition,
        width: 1.2,
        damage: 25,
      }],
    };
    const pounceMotion = {
      ...createBaseMotion("bulldrome"),
      mode: "stacking" as const,
      pounceTargetId: "brann" as const,
      pounceCenter: initialStackCenter,
    };

    for (let step = 0; step < 20; step += 1) {
      positions = updatePartyPositions(positions, [dangerMotion, pounceMotion], freshParty(), 1 + step * 0.1, 0.1);
      pounceMotion.pounceCenter = [...positions.brann];
    }

    for (const memberId of ["brann", "nia", "orin", "vale"] as const) {
      expect(distance(positions[memberId], pounceMotion.pounceCenter)).toBeLessThan(BULL_POUNCE.stackRadius);
      expect(distance(positions[memberId], initialStackCenter)).toBeGreaterThanOrEqual(1.2 + 0.55);
      expect(pointToSegmentDistance(positions[memberId], dangerMotion.slashLanes[0].start, dangerMotion.slashLanes[0].end))
        .toBeGreaterThanOrEqual(1.2 * 0.5 + 0.7);
    }
  });
});
