import { describe, expect, it } from "vitest";
import { createDefaultHealerAppearance } from "./healerVisuals";
import {
  APPEARANCE_SLOT_DEFINITIONS,
  appearanceSlotEnabled,
  appearanceSlotLabel,
  appearancesMatch,
  cycleAppearanceSlot,
} from "./appearanceLab";
import { weaponDefinitionsForSlot } from "./weaponCatalog";

describe("appearance lab choices", () => {
  it("cycles every visible slot without mutating the saved appearance", () => {
    const original = createDefaultHealerAppearance("priest");
    let draft = original;
    for (const slot of APPEARANCE_SLOT_DEFINITIONS) {
      const before = appearanceSlotLabel(draft, slot.id);
      draft = cycleAppearanceSlot(draft, slot.id, 1);
      expect(appearanceSlotLabel(draft, slot.id), slot.id).not.toBe(before);
    }
    expect(createDefaultHealerAppearance("priest")).toEqual(original);
    expect(appearancesMatch(draft, original)).toBe(false);
  });

  it("wraps choices in both directions", () => {
    const original = createDefaultHealerAppearance("druid");
    const previous = cycleAppearanceSlot(original, "main-hand", -1);
    const restored = cycleAppearanceSlot(previous, "main-hand", 1);
    expect(restored.mainHand).toEqual(original.mainHand);
  });

  it("makes every imported held design reachable without a 49-item flat cycle", () => {
    let appearance = createDefaultHealerAppearance("priest");
    const firstFamily = appearanceSlotLabel(appearance, "main-hand-family");
    const visitedFamilies = new Set<string>();
    const visitedMainHands = new Set<string>();
    do {
      const family = appearanceSlotLabel(appearance, "main-hand-family");
      expect(visitedFamilies.has(family)).toBe(false);
      visitedFamilies.add(family);
      const firstModelId = appearance.mainHand.modelId;
      do {
        visitedMainHands.add(appearance.mainHand.modelId);
        appearance = cycleAppearanceSlot(appearance, "main-hand", 1);
      } while (appearance.mainHand.modelId !== firstModelId);
      appearance = cycleAppearanceSlot(appearance, "main-hand-family", 1);
    } while (appearanceSlotLabel(appearance, "main-hand-family") !== firstFamily);

    expect(visitedFamilies).toHaveLength(10);
    expect(visitedMainHands).toHaveLength(49);
    expect(visitedMainHands).toEqual(new Set(weaponDefinitionsForSlot("main").map((entry) => entry.id)));
  });

  it("preserves a saved offhand while a two-handed style disables its row", () => {
    const paladin = createDefaultHealerAppearance("paladin");
    const twoHanded = {
      ...paladin,
      mainHand: { modelId: "cc/staff_d", grip: "staff" } as const,
    };
    expect(appearanceSlotEnabled(twoHanded, "off-hand")).toBe(false);
    expect(appearanceSlotLabel(twoHanded, "off-hand")).toBe("Hidden by two-hander");
    expect(cycleAppearanceSlot(twoHanded, "off-hand", 1)).toBe(twoHanded);
    expect(twoHanded.offHand).toEqual(paladin.offHand);
  });
});
