/**
 * NativePlayerAdapter — the Android/ExoPlayer PlayerAdapter implementation.
 *
 * ExoPlayer is driven entirely by the Rust backend (JNI), which already emits
 * PlayerStatusEvents and handles seek/audio-track internally. So this adapter is
 * a thin delegate to backend commands; there is no DOM element to touch and no
 * hls.js. State reporting is unnecessary here because the native backend emits
 * events directly — the adapter's job is only to forward control intents.
 *
 * NOTE: This adapter is currently unreachable — `createAdapter()` hardcodes the
 * HTML5 kind, so Android video runs through Html5PlayerAdapter.
 *
 * That override was introduced citing tauri#10152 as an upstream blocker. That
 * is no longer accurate: #10152 is a stale *feature request* (dead since
 * 2024-07-01) asking that `transparent` not be desktop-only, and the capability
 * shipped in tauri commit 27d01834 (2024-09-02). The related black/white-screen
 * bug (tauri#8381, #9408) was a broken JNI signature for setBackgroundColor,
 * fixed in wry 0.39.4; we ship wry 0.55.x.
 *
 * What is genuinely unproven is SurfaceView-behind-WebView *compositing* on
 * Tauri Android — nothing upstream blocks it, and nothing upstream demonstrates
 * it either. docs/specs/android-native-video-spike.md tracks that experiment.
 *
 * TRACES: UR-003, UR-005 | DR-004, DR-028
 */

import { commands } from "$lib/api/bindings";
import type { AdapterHost, PlayerAdapter, PlayerLoadOptions } from "./types";

export class NativePlayerAdapter implements PlayerAdapter {
  readonly kind = "native" as const;

  // Kept for symmetry / future reporting needs; the native backend emits events.
  private host: AdapterHost;
  private position = 0;

  constructor(host: AdapterHost) {
    this.host = host;
  }

  // The native surface is owned by the backend; nothing to attach in the DOM.
  attach(_element: HTMLVideoElement | null): void {}

  async load(_streamUrl: string, options: PlayerLoadOptions): Promise<void> {
    // player_play_item already initiated native playback before this adapter is
    // created, so there is no stream to load here — but it carries no start
    // position, and ExoPlayer always begins at 0. The resume seek must be
    // issued explicitly or "resume at position" silently plays from the top.
    //
    // Recording the position without seeking (what this used to do) is what
    // broke Android resume: the frontend believed it had resumed while
    // ExoPlayer played from the beginning.
    //
    // Live streams have no resume point — seeking one knocks the HLS window off
    // its live edge, so they are excluded.
    //
    // TRACES: UR-005 | DR-004, DR-028
    if (options.initialPosition > 0 && !options.isLive) {
      this.position = options.initialPosition;
      await commands.playerSeek(options.initialPosition);
    }
  }

  async play(): Promise<void> {
    await commands.playerPlay();
  }

  async pause(): Promise<void> {
    await commands.playerPause();
  }

  async toggle(): Promise<boolean> {
    const response = (await commands.playerToggle()) as any;
    return response?.state === "playing";
  }

  /**
   * PRIMITIVE: in-place seek. For the native backend, the backend drives
   * ExoPlayer's seek internally, so this simply records the target position.
   * (The decision to seek-in-place vs reload was already made by the backend.)
   */
  async seekElement(positionSeconds: number, _offset: number): Promise<void> {
    this.position = positionSeconds;
  }

  /**
   * PRIMITIVE: reload source. For the native backend the backend already
   * performed the reload+seek internally as part of the seek decision; nothing
   * to do on the frontend beyond recording position.
   */
  async reloadSource(_url: string, offset: number): Promise<void> {
    this.position = offset;
  }

  setVolume(volume: number): void {
    void commands.playerSetVolume(Math.max(0, Math.min(1, volume)));
  }

  setMuted(_muted: boolean): void {
    void commands.playerToggleMute();
  }

  async selectSubtitle(streamIndex: number | null, arrayIndex?: number): Promise<void> {
    const indexToUse = streamIndex === null ? null : arrayIndex ?? streamIndex;
    await commands.playerSetSubtitleTrack(indexToUse);
  }

  getPosition(): number {
    return this.position;
  }

  async dispose(): Promise<void> {
    // The backend is stopped via player_stop by the owning view; nothing to free.
  }
}
