// Playback reporting service
//
// Simplified service that delegates all logic to the Rust backend.
// The backend handles:
// - Local DB updates
// - Jellyfin server reporting
// - Offline queueing (via sync queue)
// - Connectivity checks
//
// TRACES: UR-005, UR-019, UR-025 | DR-028, DR-047

import { commands } from "$lib/api/bindings";
import { auth } from "$lib/stores/auth";

/**
 * Record the start of playback **locally**, with the context it started from.
 *
 * The server is told by Rust: loading an item into the controller reports the
 * start, carrying the position the stream actually begins at (zero for an
 * ordinary play, the handoff point for a background-audio stream). What this
 * adds is the context — which album or series the play came from — which only
 * the local DB keeps.
 *
 * TRACES: UR-005, UR-025 | DR-028, DR-179
 */
export async function reportPlaybackStart(
  itemId: string,
  positionSeconds: number,
  contextType: "container" | "single" = "single",
  contextId: string | null = null
): Promise<void> {
  const positionMs = Math.floor(positionSeconds * 1000);
  const userId = auth.getUserId();

  console.log(
    "[PlaybackReporting] reportPlaybackStart - itemId:",
    itemId,
    "positionSeconds:",
    positionSeconds,
    "context:",
    contextType,
    contextId
  );

  // Update local DB with context (always works, even offline)
  if (userId) {
    try {
      await commands.storageUpdatePlaybackContext(userId, itemId, positionMs, contextType, contextId);
    } catch (e) {
      console.error("[PlaybackReporting] Failed to update playback context:", e);
    }
  }
}

/**
 * Record playback progress **locally**.
 *
 * The server's copy is not sent from here. Position ticks already flow into Rust
 * through the player adapter (`player_report_position`), and the controller
 * reports them onward on a 30s throttle — one place that covers webview video,
 * native audio and the background-audio handoff alike, instead of a second
 * frequent IPC path racing it.
 *
 * This function is therefore the *local* half only, which is what its caller
 * needs for resume points that work offline.
 *
 * TRACES: UR-005 | DR-028, DR-179
 */
export async function reportPlaybackProgress(
  itemId: string,
  positionSeconds: number,
  _isPaused = false
): Promise<void> {
  const positionMs = Math.floor(positionSeconds * 1000);
  const userId = auth.getUserId();

  // Reduce logging for frequent progress updates
  if (Math.floor(positionSeconds) % 30 === 0) {
    console.log("[PlaybackReporting] reportPlaybackProgress - itemId:", itemId, "position:", positionSeconds);
  }

  // Update local DB only (progress updates are frequent, don't report to server)
  if (userId) {
    try {
      await commands.storageUpdatePlaybackProgress(userId, itemId, positionMs);
    } catch (e) {
      console.error("[PlaybackReporting] Failed to update local progress:", e);
    }
  }
}

/**
 * Report playback stopped to Jellyfin (or queue if offline)
 *
 * The Rust backend handles both local DB updates and server reporting,
 * automatically queuing for sync if the server is unreachable.
 *
 * TRACES: UR-005, UR-025 | DR-028
 */
export async function reportPlaybackStopped(itemId: string, positionSeconds: number): Promise<void> {
  const positionMs = Math.floor(positionSeconds * 1000);
  const userId = auth.getUserId();

  console.log("[PlaybackReporting] reportPlaybackStopped - itemId:", itemId, "positionSeconds:", positionSeconds);

  // Update local DB first (always works, even offline)
  if (userId) {
    try {
      await commands.storageUpdatePlaybackProgress(userId, itemId, positionMs);
    } catch (e) {
      console.error("[PlaybackReporting] Failed to update local progress:", e);
    }
  }

  // Report to the server. Rust queues the position for the next reconnect if
  // the server cannot be reached (DR-154), so a throw here means the report
  // did not land *this time* — not that the position was lost.
  if (userId && positionSeconds > 0) {
    try {
      const repo = auth.getRepository();
      await repo.reportPlaybackStopped(itemId, positionMs);
    } catch (e) {
      console.warn("[PlaybackReporting] Stop-report did not reach the server; queued for sync:", e);
    }
  }
}

/**
 * Mark an item as played (100% progress)
 *
 * TRACES: UR-025 | DR-028
 */
export async function markAsPlayed(itemId: string): Promise<void> {
  const userId = auth.getUserId();

  console.log("[PlaybackReporting] markAsPlayed - itemId:", itemId);

  // Update local DB first
  if (userId) {
    try {
      await commands.storageMarkPlayed(userId, itemId);
    } catch (e) {
      console.error("[PlaybackReporting] Failed to mark as played in local DB:", e);
    }
  }

  // Try to report to server via repository (handles queuing internally)
  try {
    const repo = auth.getRepository();
    const item = await repo.getItem(itemId);

    if (item.durationMs) {
      await repo.reportPlaybackStopped(itemId, item.durationMs);
    }
  } catch (e) {
    console.error("[PlaybackReporting] Failed to report as played:", e);
  }
}
