/**
 * Unit tests for NativePlayerAdapter — thin delegate to backend commands.
 * Pins the primitive→command mapping so the ExoPlayer path stays correct.
 */

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

const playerPlay = vi.fn((..._a: any[]): any => ({}));
const playerPause = vi.fn((..._a: any[]): any => ({}));
const playerToggle = vi.fn((..._a: any[]): any => ({ state: "playing" }));
const playerSetVolume = vi.fn((..._a: any[]): any => ({}));
const playerToggleMute = vi.fn((..._a: any[]): any => ({}));
const playerSetSubtitleTrack = vi.fn((..._a: any[]): any => ({}));

vi.mock("$lib/api/bindings", () => ({
  commands: {
    playerPlay: (...a: any[]) => playerPlay(...a),
    playerPause: (...a: any[]) => playerPause(...a),
    playerToggle: (...a: any[]) => playerToggle(...a),
    playerSetVolume: (...a: any[]) => playerSetVolume(...a),
    playerToggleMute: (...a: any[]) => playerToggleMute(...a),
    playerSetSubtitleTrack: (...a: any[]) => playerSetSubtitleTrack(...a),
  },
}));

import { NativePlayerAdapter } from "./nativeAdapter";
import type { AdapterHost } from "./types";

function makeHost(): AdapterHost {
  return {
    onState: vi.fn(), onPosition: vi.fn(), onMediaLoaded: vi.fn(), onEnded: vi.fn(),
    onError: vi.fn(), onStreamUrlChanged: vi.fn(), onBuffering: vi.fn(), onReady: vi.fn(),
  };
}

describe("NativePlayerAdapter", () => {
  let adapter: NativePlayerAdapter;
  beforeEach(() => {
    vi.clearAllMocks();
    adapter = new NativePlayerAdapter(makeHost());
  });

  it("is a native-kind adapter", () => {
    expect(adapter.kind).toBe("native");
  });

  it("delegates play/pause to backend commands", async () => {
    await adapter.play();
    await adapter.pause();
    expect(playerPlay).toHaveBeenCalledTimes(1);
    expect(playerPause).toHaveBeenCalledTimes(1);
  });

  it("toggle() reflects the backend's resulting playing state", async () => {
    expect(await adapter.toggle()).toBe(true);
    expect(playerToggle).toHaveBeenCalledTimes(1);
  });

  it("records position on seek/reload primitives (backend does the real work)", async () => {
    await adapter.seekElement(55, 0);
    expect(adapter.getPosition()).toBe(55);
    await adapter.reloadSource("ignored", 200);
    expect(adapter.getPosition()).toBe(200);
  });

  it("load() seeds a resume position", async () => {
    await adapter.load("url", {
      mediaId: "m", mediaSourceId: null, needsTranscoding: false,
      initialPosition: 90, isLive: false, audioTrackIndex: null,
      knownDuration: 0, subtitleTracks: [],
    });
    expect(adapter.getPosition()).toBe(90);
  });

  it("setVolume clamps and delegates; setMuted toggles mute", () => {
    adapter.setVolume(2);
    expect(playerSetVolume).toHaveBeenCalledWith(1);
    adapter.setMuted(true);
    expect(playerToggleMute).toHaveBeenCalledTimes(1);
  });

  it("selectSubtitle maps null to disable and uses arrayIndex when given", async () => {
    await adapter.selectSubtitle(null);
    expect(playerSetSubtitleTrack).toHaveBeenCalledWith(null);
    await adapter.selectSubtitle(5, 2);
    expect(playerSetSubtitleTrack).toHaveBeenCalledWith(2);
  });
});
