using DeepDrftModels.DTOs;

namespace DeepDrftPublic.Client.Services;

/// <summary>
/// Orchestrates ordered playback ("what plays next") <em>above</em> the single-slot
/// <see cref="IStreamingPlayerService"/>. 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 <see cref="IStreamingPlayerService.SelectTrackStreaming"/>
/// — it adds no new playback semantics.
///
/// <para>
/// Extension posture (open/closed): future shuffle, repeat modes, reordering, and persistence are
/// expected. They are additive — a shuffle/repeat strategy slots in behind <see cref="Next"/>/
/// <see cref="Previous"/> as the "which index is next" decision; reordering mutates <see cref="Items"/>
/// and re-emits <see cref="QueueChanged"/>; persistence snapshots/restores <see cref="Items"/> +
/// <see cref="CurrentIndex"/>. None of those require changing this interface's existing members, only
/// adding new ones — so consumers written against today's surface keep working.
/// </para>
///
/// <para>
/// With an empty queue (<see cref="CurrentIndex"/> == -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.
/// </para>
/// </summary>
public interface IQueueService
{
    /// <summary>The ordered tracks currently queued. Empty when nothing is enqueued.</summary>
    IReadOnlyList<TrackDto> Items { get; }

    /// <summary>
    /// Index into <see cref="Items"/> of the track the queue considers current, or -1 when the
    /// queue is empty. Always a valid index into <see cref="Items"/> when non-negative.
    /// </summary>
    int CurrentIndex { get; }

    /// <summary>The current track, or null when the queue is empty.</summary>
    TrackDto? Current { get; }

    /// <summary>True when there is a track after <see cref="CurrentIndex"/> to advance to.</summary>
    bool HasNext { get; }

    /// <summary>True when there is a track before <see cref="CurrentIndex"/> to step back to.</summary>
    bool HasPrevious { get; }

    /// <summary>
    /// 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.
    /// </summary>
    event Action? QueueChanged;

    /// <summary>
    /// Replaces the queue with <paramref name="tracks"/> (in the order given) and begins streaming
    /// the track at <paramref name="startIndex"/>. This is the "play album" entry point the Cuts
    /// detail page consumes: pass the release's tracks in ordinal order. A header Play uses
    /// <c>startIndex: 0</c>; a mid-album row play passes that row's index so the queue continues to
    /// the end from there. No-op when <paramref name="tracks"/> is empty.
    /// </summary>
    Task PlayRelease(IEnumerable<TrackDto> tracks, int startIndex = 0);

    /// <summary>Appends a track to the end of the queue without changing what is currently playing.</summary>
    void Enqueue(TrackDto track);

    /// <summary>Appends tracks to the end of the queue without changing what is currently playing.</summary>
    void EnqueueRange(IEnumerable<TrackDto> tracks);

    /// <summary>
    /// Advances to the next track and streams it. No-op when <see cref="HasNext"/> is false.
    /// </summary>
    Task Next();

    /// <summary>
    /// Steps back to the previous track and streams it. No-op when <see cref="HasPrevious"/> is false.
    /// </summary>
    Task Previous();

    /// <summary>Empties the queue and resets the position. Does not stop the player.</summary>
    void Clear();
}
