import { describe, expect, it } from "vitest";
import { ARENA_CENTER, ARENA_RADIUS, ARENA_WALL_RADIUS, clampToArena } from "../arena";
import type { WorldPosition } from "../types";
import {
  BOSS_ARENA_PORTAL_EXIT_DEPTH,
  BOSS_ARENA_PORTAL_HALF_WIDTH,
  BOSS_ARENA_PORTAL_OUTER_DEPTH,
  clampToBossArenaWithPortals,
  detectBossArenaExit,
  type BossArenaPortalOpenState,
} from "./playSpace";

const CLOSED: BossArenaPortalOpenState = { north: false, south: false };
const NORTH_OPEN: BossArenaPortalOpenState = { north: true, south: false };
const SOUTH_OPEN: BossArenaPortalOpenState = { north: false, south: true };

describe("RPG roguelike boss arena play space", () => {
  it("preserves the existing arena clamp while both portals are closed", () => {
    const samples: WorldPosition[] = [
      [0, ARENA_CENTER[1]],
      [0, ARENA_CENTER[1] - ARENA_WALL_RADIUS],
      [0.8, ARENA_CENTER[1] + ARENA_WALL_RADIUS + 2],
      [ARENA_WALL_RADIUS, ARENA_CENTER[1]],
    ];

    for (const sample of samples) {
      expect(clampToBossArenaWithPortals(sample, CLOSED, 0.35)).toEqual(clampToArena(sample, 0.35));
    }
  });

  it("allows travel through only the corresponding open rectangular throat", () => {
    const northThroat: WorldPosition = [0, ARENA_CENTER[1] - ARENA_WALL_RADIUS];
    const southThroat: WorldPosition = [0, ARENA_CENTER[1] + ARENA_WALL_RADIUS];

    expect(clampToBossArenaWithPortals(northThroat, NORTH_OPEN)).toEqual(northThroat);
    expect(clampToBossArenaWithPortals(southThroat, SOUTH_OPEN)).toEqual(southThroat);
    expect(clampToBossArenaWithPortals(southThroat, NORTH_OPEN)).toEqual(clampToArena(southThroat));
    expect(clampToBossArenaWithPortals(northThroat, SOUTH_OPEN)).toEqual(clampToArena(northThroat));
  });

  it("keeps an open throat narrow and clamps its outer end", () => {
    const beyondSide: WorldPosition = [BOSS_ARENA_PORTAL_HALF_WIDTH + 2, ARENA_CENTER[1] - ARENA_WALL_RADIUS];
    const beyondEnd: WorldPosition = [0, ARENA_CENTER[1] - ARENA_WALL_RADIUS - BOSS_ARENA_PORTAL_OUTER_DEPTH - 4];
    const sideResult = clampToBossArenaWithPortals(beyondSide, NORTH_OPEN);
    const endResult = clampToBossArenaWithPortals(beyondEnd, NORTH_OPEN);

    expect(sideResult).not.toEqual(beyondSide);
    expect(sideResult[0]).toBeLessThanOrEqual(BOSS_ARENA_PORTAL_HALF_WIDTH);
    expect(endResult).toEqual([
      0,
      ARENA_CENTER[1] - ARENA_WALL_RADIUS - BOSS_ARENA_PORTAL_OUTER_DEPTH,
    ]);
  });

  it("keeps ordinary in-arena movement unchanged when a portal is open", () => {
    const position: WorldPosition = [ARENA_RADIUS * 0.4, ARENA_CENTER[1] + ARENA_RADIUS * 0.25];
    expect(clampToBossArenaWithPortals(position, NORTH_OPEN)).toEqual(position);
  });

  it("detects exits only after crossing an open portal's exit plane", () => {
    const northExitZ = ARENA_CENTER[1] - ARENA_WALL_RADIUS - BOSS_ARENA_PORTAL_EXIT_DEPTH;
    const southExitZ = ARENA_CENTER[1] + ARENA_WALL_RADIUS + BOSS_ARENA_PORTAL_EXIT_DEPTH;

    expect(detectBossArenaExit([0, northExitZ], NORTH_OPEN)).toBe("north");
    expect(detectBossArenaExit([0, southExitZ], SOUTH_OPEN)).toBe("south");
    expect(detectBossArenaExit([0, northExitZ + 0.01], NORTH_OPEN)).toBeNull();
    expect(detectBossArenaExit([0, northExitZ], CLOSED)).toBeNull();
    expect(detectBossArenaExit([BOSS_ARENA_PORTAL_HALF_WIDTH + 0.01, northExitZ], NORTH_OPEN)).toBeNull();
  });
});

