import { describe, it, expect } from "vitest";
import {
  computeHandoffPosition,
  planHandoffReturn,
  initialHandoffState,
  shouldEnterBackgroundAudio,
  shouldExitBackgroundAudio,
  shouldResumeOnForeground,
  type BackgroundAudioState,
} from "./backgroundAudioHandoff";

// TRACES: UR-040 | DR-052 | UT-060

describe("backgroundAudioHandoff", () => {
  describe("computeHandoffPosition", () => {
    it("sums element time and transcode seekOffset (absolute position)", () => {
      // Transcoded HLS resets element time to 0 after a reload; seekOffset carries
      // the cumulative offset. The audio stream must resume at the absolute pos.
      expect(computeHandoffPosition(12, 180)).toBe(192);
    });

    it("handles a direct stream with no offset", () => {
      expect(computeHandoffPosition(45, 0)).toBe(45);
    });

    it("never returns a negative position", () => {
      expect(computeHandoffPosition(-5, 0)).toBe(0);
    });
  });

  describe("shouldEnterBackgroundAudio", () => {
    it("enters when toggle is on and not already handed off", () => {
      expect(shouldEnterBackgroundAudio(true, initialHandoffState)).toBe(true);
    });

    it("does not enter when the toggle is off", () => {
      expect(shouldEnterBackgroundAudio(false, initialHandoffState)).toBe(false);
    });

    it("does not double-enter when already active", () => {
      const active: BackgroundAudioState = { active: true, wasPlaying: true };
      expect(shouldEnterBackgroundAudio(true, active)).toBe(false);
    });
  });

  describe("shouldExitBackgroundAudio", () => {
    it("exits when a handoff is active", () => {
      const active: BackgroundAudioState = { active: true, wasPlaying: false };
      expect(shouldExitBackgroundAudio(active)).toBe(true);
    });

    it("does not exit when no handoff happened", () => {
      expect(shouldExitBackgroundAudio(initialHandoffState)).toBe(false);
    });

    it("exits even if the toggle was turned off while backgrounded", () => {
      // shouldExit ignores the toggle by design, so turning it off mid-background
      // still returns cleanly to video on foreground.
      const active: BackgroundAudioState = { active: true, wasPlaying: true };
      expect(shouldExitBackgroundAudio(active)).toBe(true);
    });
  });

  describe("shouldResumeOnForeground", () => {
    it("resumes when it was playing and the native audio still is", () => {
      expect(shouldResumeOnForeground(true, "playing")).toBe(true);
    });

    it("stays paused when the lockscreen paused the native audio", () => {
      // The whole point of the lockscreen pause: coming back to the app must not
      // undo it just because the video was playing when we handed off.
      expect(shouldResumeOnForeground(true, "paused")).toBe(false);
    });

    it("stays paused when the video was already paused at handoff", () => {
      expect(shouldResumeOnForeground(false, "playing")).toBe(false);
    });

    it("falls back to the captured state when native state is unknown", () => {
      // Loading/seeking/idle say nothing about intent — the handoff snapshot is
      // the best evidence we have, so a playing video still resumes.
      expect(shouldResumeOnForeground(true, "loading")).toBe(true);
      expect(shouldResumeOnForeground(true, undefined)).toBe(true);
    });
  });

  // Returning from background audio has to restart whatever is actually
  // rendering. The webview <video> reloads off its stream URL, but the native
  // (ExoPlayer) path owns no element and no URL-driven effect — its playback is
  // only ever started by an explicit backend load. The component used to just
  // reassign the stream URL and call it done, which on the native path restarted
  // nothing: the player sat IDLE on a black screen with a play overlay, and the
  // play button did nothing because the backend held no item.
  //
  // TRACES: UR-040, UR-003 | DR-196
  describe("planHandoffReturn", () => {
    it("restarts the native backend when the native path is rendering", () => {
      const plan = planHandoffReturn({
        useHtml5Element: false,
        position: 4214,
        wasPlaying: true,
        nativeStateKind: "playing",
      });
      expect(plan.target).toBe("native-backend");
      expect(plan.position).toBe(4214);
      expect(plan.shouldPlay).toBe(true);
    });

    it("reloads the webview element when HTML5 is rendering", () => {
      const plan = planHandoffReturn({
        useHtml5Element: true,
        position: 120,
        wasPlaying: true,
        nativeStateKind: "playing",
      });
      expect(plan.target).toBe("html5-element");
    });

    it("honours a lockscreen pause over the handoff snapshot", () => {
      const plan = planHandoffReturn({
        useHtml5Element: false,
        position: 300,
        wasPlaying: true,
        nativeStateKind: "paused",
      });
      expect(plan.shouldPlay).toBe(false);
    });

    it("never returns a negative resume position", () => {
      const plan = planHandoffReturn({
        useHtml5Element: false,
        position: -3,
        wasPlaying: false,
        nativeStateKind: undefined,
      });
      expect(plan.position).toBe(0);
    });
  });
});
