using DeepDrftModels.DTOs;
namespace DeepDrftPublic.Client.Services;
///
/// Orchestrates ordered playback ("what plays next") above the single-slot
/// . The player stays a single-track device; the queue owns the
/// track list, the current position, skip-forward/back, and auto-advance on natural track end. It
/// drives playback solely through the player's existing
/// — it adds no new playback semantics.
///
///
/// Extension posture (open/closed): future shuffle, repeat modes, reordering, and persistence are
/// expected. They are additive — a shuffle/repeat strategy slots in behind /
/// as the "which index is next" decision; reordering mutates
/// and re-emits ; persistence snapshots/restores +
/// . None of those require changing this interface's existing members, only
/// adding new ones — so consumers written against today's surface keep working.
///
///
///
/// With an empty queue ( == -1) the queue is dormant: it drives nothing and
/// auto-advances nothing, so direct single-track play through the player behaves exactly as it did
/// before the queue existed.
///
///
public interface IQueueService
{
/// The ordered tracks currently queued. Empty when nothing is enqueued.
IReadOnlyList Items { get; }
///
/// Index into of the track the queue considers current, or -1 when the
/// queue is empty. Always a valid index into when non-negative.
///
int CurrentIndex { get; }
/// The current track, or null when the queue is empty.
TrackDto? Current { get; }
///
/// True when the queue has been loaded via but no track has streamed yet —
/// the embed's pre-gesture state. Set by ; cleared the moment playback actually
/// starts (///)
/// or on . The player bar reads this to route the first play gesture through
/// (which begins the armed release) rather than streaming the staged track alone.
///
bool IsArmed { get; }
/// True when there is a track after to advance to.
bool HasNext { get; }
/// True when there is a track before to step back to.
bool HasPrevious { get; }
///
/// Raised whenever the queue's contents or current position change. The player bar subscribes
/// to re-render its skip-forward/back affordances. Fires on enqueue, advance, step-back, and clear.
///
event Action? QueueChanged;
///
/// Replaces the queue with (in the order given) and begins streaming
/// the track at . This is the "play album" entry point the Cuts
/// detail page consumes: pass the release's tracks in ordinal order. A header Play uses
/// startIndex: 0; a mid-album row play passes that row's index so the queue continues to
/// the end from there. No-op when is empty.
///
Task PlayRelease(IEnumerable tracks, int startIndex = 0);
///
/// Loads as the queue and sets the current position to index 0 WITHOUT
/// streaming anything — the queue is "armed". This is the embed's prerender-safe entry point: it
/// performs no JS interop, so it runs identically during prerender and after WASM boot. The first
/// play gesture (see ) then starts playback via , which
/// keeps the loaded release queued so it advances through its tracks. No-op when
/// is empty (the queue stays empty and disarmed).
///
void Arm(IEnumerable tracks);
///
/// Begins playback of an armed queue (see ): streams the current track and clears
/// , leaving the loaded list and position intact so auto-advance carries on
/// through the release. This is the first-gesture entry point the embed bar calls. No-op (and stays
/// disarmed) when the queue is not armed or is empty — so it never double-streams or disturbs a
/// queue already playing.
///
Task Start();
/// Appends a track to the end of the queue without changing what is currently playing.
void Enqueue(TrackDto track);
/// Appends tracks to the end of the queue without changing what is currently playing.
void EnqueueRange(IEnumerable tracks);
///
/// Advances to the next track and streams it. No-op when is false.
///
Task Next();
///
/// Steps back to the previous track and streams it. No-op when is false.
///
Task Previous();
/// Empties the queue and resets the position. Does not stop the player.
void Clear();
}