/**
 * PlayerAdapter contract — the decoupled boundary between the UI/backend and a
 * concrete video player implementation (Linux HTML5+hls.js, or Android native).
 *
 * The whole point: UI components and the Rust backend interact with video ONLY
 * through this interface. All element / hls.js / ExoPlayer / textTracks detail —
 * and the backend seek/audio-track *strategy* round-trip — is internal to an
 * implementation. A control intent (from UI or a backend lockscreen/remote/sleep
 * event) reaches the element by the facade dispatching to the active adapter.
 *
 * State flows OUTWARD through the {@link AdapterHost} callback bag rather than the
 * adapter importing stores/commands directly — this keeps adapters unit-testable
 * with a mock host and keeps the reporting-to-Rust wiring in one place.
 *
 * TRACES: UR-003, UR-005, UR-020, UR-021 | DR-001, DR-023, DR-024, DR-028
 */

/** A subtitle track handed to the adapter at load time (WebVTT for HTML5). */
export interface SubtitleTrackInput {
  index: number;
  url: string;
  language: string | null;
  label: string;
  mimeType: string;
}

/** Everything an adapter needs to load and begin a stream. */
export interface PlayerLoadOptions {
  /** Jellyfin item id — used as the media_id when reporting state to Rust. */
  mediaId: string;
  /** Media source id for subtitle/seek URLs (null for local/direct). */
  mediaSourceId: string | null;
  /** HEVC/10-bit content that needs server transcoding (affects seek strategy). */
  needsTranscoding: boolean;
  /** Resume position in seconds (0 = start from beginning). */
  initialPosition: number;
  /** Live stream — no seek bar, no resume, no progress reporting. */
  isLive: boolean;
  /** Preselected audio track stream index, or null for the default. */
  audioTrackIndex: number | null;
  /** Known total duration in seconds (from runTimeTicks), or 0 if unknown. */
  knownDuration: number;
  /** Subtitle tracks available for this media. */
  subtitleTracks: SubtitleTrackInput[];
}

/**
 * Callback bag the adapter uses to report the element's lifecycle outward. The
 * facade supplies an implementation that forwards to Rust (via the
 * `player_report_*` commands) and, where needed, to the UI.
 */
export interface AdapterHost {
  /** Playback state changed (playing/paused/loading/stopped/idle). */
  onState(state: "playing" | "paused" | "loading" | "stopped" | "idle"): void;
  /** Position/duration tick (adapter throttles; host forwards to Rust). */
  onPosition(position: number, duration: number): void;
  /** Media finished loading and knows its duration. */
  onMediaLoaded(duration: number): void;
  /** Playback reached the natural end of the stream (fires at most once). */
  onEnded(): void;
  /** A non-fatal or fatal playback error occurred. */
  onError(message: string): void;
  /**
   * The stream URL the adapter is now playing changed (e.g. transcode reload on
   * seek/audio-track switch). Lets the owning view keep its `<video src>` in sync.
   */
  onStreamUrlChanged(url: string): void;
  /** Buffering/ready transitions, so the view can show/hide its spinner. */
  onBuffering(isBuffering: boolean): void;
  onReady(): void;
}

/**
 * One concrete player implementation per platform. Methods are high-level
 * intents; strategy objects, hls instances, and textTracks never cross this line.
 */
export interface PlayerAdapter {
  /** Which platform backend this adapter represents. */
  readonly kind: "html5" | "native";

  /**
   * Bind the output target. For the HTML5 adapter this is the `<video>` element
   * (pass null on teardown); the native adapter ignores it (ExoPlayer renders to
   * its own surface).
   */
  attach(element: HTMLVideoElement | null): void;

  /** Load a stream and begin playback at `options.initialPosition`. */
  load(streamUrl: string, options: PlayerLoadOptions): Promise<void>;

  play(): Promise<void>;
  pause(): Promise<void>;
  /** Toggle play/pause; resolves to the resulting playing state. */
  toggle(): Promise<boolean>;

  // --- Seek/reload PRIMITIVES (decision-free) ---------------------------------
  // The backend DECIDES whether a seek is an in-place element seek or a full
  // source reload (transcode). The adapter only executes the chosen primitive;
  // it contains no strategy branch. This is what keeps the decision logic shared
  // in Rust (Option 1).

  /**
   * In-place seek of the already-loaded source (no reload). `offset` is the
   * transcode seek offset the element position is relative to (0 for direct).
   */
  seekElement(positionSeconds: number, offset: number): Promise<void>;

  /**
   * Compound reload: swap to `url` and resume at `offset` seconds. Runs the
   * invariant mechanical sequence for this platform (html5: pause → hls teardown
   * → clear src → set new url → wait ready → resume; native: ExoPlayer setMediaItem
   * + seekTo). No decision is made here — the backend already decided to reload.
   */
  reloadSource(url: string, offset: number): Promise<void>;

  setVolume(volume: number): void; // 0..1
  setMuted(muted: boolean): void;

  /** Enable a subtitle track (null disables) — DOM textTracks is a webview primitive. */
  selectSubtitle(streamIndex: number | null, arrayIndex?: number): Promise<void>;

  /** Current position in seconds (adapter's own truth, e.g. element.currentTime + offset). */
  getPosition(): number;

  /** Tear down: destroy hls, detach element, stop reporting. Idempotent. */
  dispose(): Promise<void>;
}
