import { describe, expect, it } from "vitest";

import { DEFAULT_VOLUME, normalizeVolume, stepVolume } from "./playerVolume";

describe("normalizeVolume", () => {
  it("keeps a valid level", () => {
    expect(normalizeVolume(0)).toBe(0);
    expect(normalizeVolume(37)).toBe(37);
    expect(normalizeVolume(100)).toBe(100);
  });

  it("clamps out-of-range levels and rounds to whole percents", () => {
    expect(normalizeVolume(-20)).toBe(0);
    expect(normalizeVolume(150)).toBe(100);
    expect(normalizeVolume(42.6)).toBe(43);
  });

  it.each([
    ["a missing key", undefined],
    ["an explicit null", null],
    ["a stringified number", "80"],
    ["NaN from a broken player", NaN],
    ["a whole object", { volume: 50 }],
  ])("falls back on %s", (_label, raw) => {
    // The regression: a partial stored object made the level undefined, which Math.round turned
    // into NaN — muting playback and persisting `null` for every later session.
    expect(normalizeVolume(raw, 15)).toBe(15);
  });

  it("prefers the last known level over the default, so a quiet session stays quiet", () => {
    expect(normalizeVolume(NaN, 15)).toBe(15);
    expect(normalizeVolume(NaN, 15)).not.toBe(DEFAULT_VOLUME);
  });

  it("uses the default when the fallback is unusable too", () => {
    expect(normalizeVolume(NaN, NaN)).toBe(DEFAULT_VOLUME);
    expect(normalizeVolume(null, undefined)).toBe(DEFAULT_VOLUME);
    expect(normalizeVolume(null, "80")).toBe(DEFAULT_VOLUME);
  });
});

describe("stepVolume", () => {
  it("steps up and down from the current level", () => {
    expect(stepVolume(60, { delta: 5, fallback: 100 })).toBe(65);
    expect(stepVolume(60, { delta: -5, fallback: 100 })).toBe(55);
  });

  it("clamps at both ends instead of wrapping", () => {
    expect(stepVolume(98, { delta: 5, fallback: 100 })).toBe(100);
    expect(stepVolume(3, { delta: -5, fallback: 100 })).toBe(0);
  });

  it("steps from the fallback when the player answers garbage", () => {
    // A spin must not lose notches to a stale/unusable reading — it continues from what we last set.
    expect(stepVolume(undefined, { delta: -5, fallback: 90 })).toBe(85);
    expect(stepVolume(NaN, { delta: -5, fallback: 90 })).toBe(85);
  });
});
