use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};

use super::media::{MediaItem, MediaSource, QueueContext};

/// Repeat mode for the queue
///
/// TRACES: UR-005 | DR-005
#[derive(specta::Type, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum RepeatMode {
    #[default]
    Off,
    All,
    One,
}

/// Queue manager for playlist functionality
///
/// TRACES: UR-005, UR-015 | DR-005, DR-020
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize)]
pub struct QueueManager {
    /// All items in the queue
    items: Vec<MediaItem>,
    /// Current item index
    current_index: Option<usize>,
    /// Whether shuffle is enabled
    shuffle: bool,
    /// Current repeat mode
    repeat: RepeatMode,
    /// Shuffled order of indices (used when shuffle is on)
    shuffle_order: Vec<usize>,
    /// History of played indices (for going back with shuffle)
    history: Vec<usize>,
    /// Context for the queue (album, playlist, or custom)
    /// Used for remote playback transfer to maintain album/playlist context
    #[serde(default)]
    context: QueueContext,
}

impl Default for QueueManager {
    fn default() -> Self {
        Self::new()
    }
}

impl QueueManager {
    pub fn new() -> Self {
        Self {
            items: Vec::new(),
            current_index: None,
            shuffle: false,
            repeat: RepeatMode::Off,
            shuffle_order: Vec::new(),
            history: Vec::new(),
            context: QueueContext::Custom,
        }
    }

    /// Get all items in the queue
    pub fn items(&self) -> &[MediaItem] {
        &self.items
    }

    /// Mutable access to queue items.
    ///
    /// Used to re-point streaming entries at a completed local download
    /// without disturbing queue order, shuffle state, or the current index.
    pub fn items_mut(&mut self) -> &mut [MediaItem] {
        &mut self.items
    }

    /// Get the current item index
    pub fn current_index(&self) -> Option<usize> {
        self.current_index
    }

    /// Get the current item
    pub fn current(&self) -> Option<&MediaItem> {
        self.current_index.and_then(|i| self.items.get(i))
    }

    /// Check if shuffle is enabled
    pub fn is_shuffle(&self) -> bool {
        self.shuffle
    }

    /// Get the current repeat mode
    pub fn repeat_mode(&self) -> RepeatMode {
        self.repeat
    }

    /// Get the current queue context (album, playlist, or custom)
    pub fn context(&self) -> &QueueContext {
        &self.context
    }

    /// Set the queue context
    pub fn set_context(&mut self, context: QueueContext) {
        self.context = context;
    }

    /// Set the queue with new items (resets context to Custom)
    pub fn set_queue(&mut self, items: Vec<MediaItem>, start_index: usize) {
        self.set_queue_with_context(items, start_index, QueueContext::Custom);
    }

    /// Set the queue with new items and explicit context
    pub fn set_queue_with_context(
        &mut self,
        items: Vec<MediaItem>,
        start_index: usize,
        context: QueueContext,
    ) {
        let start_index = start_index.min(items.len().saturating_sub(1));

        if self.shuffle && !items.is_empty() {
            self.shuffle_order = self.generate_shuffle_order(items.len(), Some(start_index));
        } else {
            self.shuffle_order.clear();
        }

        self.items = items;
        self.current_index = if self.items.is_empty() {
            None
        } else {
            Some(start_index)
        };
        self.history.clear();
        self.context = context;
    }

    /// Clear the queue entirely, returning it to the empty state.
    ///
    /// Used when playback genuinely stops (sleep timer fires, or the queue ends
    /// with repeat off) so the frontend's `currentQueueItem` becomes null and
    /// the mini player hides. History and shuffle order are reset too.
    pub fn clear(&mut self) {
        self.items.clear();
        self.current_index = None;
        self.history.clear();
        self.shuffle_order.clear();
    }

    /// Add items to the queue
    pub fn add(&mut self, items: Vec<MediaItem>, position: AddPosition) {
        if items.is_empty() {
            return;
        }

        let insert_index = match position {
            AddPosition::Next => self
                .current_index
                .map(|i| i + 1)
                .unwrap_or(self.items.len()),
            AddPosition::End => self.items.len(),
        };

        // Insert items
        for (i, item) in items.into_iter().enumerate() {
            self.items.insert(insert_index + i, item);
        }

        // Update current index if needed
        if let Some(current) = self.current_index {
            if insert_index <= current {
                self.current_index = Some(current + 1);
            }
        }

        // Regenerate shuffle order if shuffle is on
        if self.shuffle {
            self.shuffle_order = self.generate_shuffle_order(self.items.len(), self.current_index);
        }
    }

    /// Remove an item from the queue
    pub fn remove(&mut self, index: usize) -> Option<MediaItem> {
        if index >= self.items.len() {
            return None;
        }

        let removed = self.items.remove(index);

        // Update current index
        if let Some(current) = self.current_index {
            if index < current {
                self.current_index = Some(current - 1);
            } else if index == current {
                self.current_index = if self.items.is_empty() {
                    None
                } else if index >= self.items.len() {
                    Some(self.items.len() - 1)
                } else {
                    Some(index)
                };
            }
        }

        // Update shuffle order
        if self.shuffle {
            self.shuffle_order = self
                .shuffle_order
                .iter()
                .filter(|&&i| i != index)
                .map(|&i| if i > index { i - 1 } else { i })
                .collect();
        }

        Some(removed)
    }

    /// Move to the next item
    pub fn next(&mut self) -> Option<&MediaItem> {
        if self.items.is_empty() {
            return None;
        }

        let current = self.current_index?;

        let next_index = if self.repeat == RepeatMode::One {
            // Repeat current track
            current
        } else if self.shuffle {
            // Find current position in shuffle order and get next
            let pos = self.shuffle_order.iter().position(|&i| i == current)?;
            if pos + 1 < self.shuffle_order.len() {
                self.shuffle_order[pos + 1]
            } else if self.repeat == RepeatMode::All {
                // Wrap around to beginning of shuffle
                self.shuffle_order[0]
            } else {
                log::debug!("[Queue] next() at end of shuffle order, no next track");
                return None;
            }
        } else {
            // Normal sequential order
            if current + 1 < self.items.len() {
                current + 1
            } else if self.repeat == RepeatMode::All {
                0
            } else {
                log::debug!(
                    "[Queue] next() at end of queue (index {}), no next track",
                    current
                );
                return None;
            }
        };

        // Only add to history if we're actually changing position
        if next_index != current {
            self.history.push(current);
            log::debug!("[Queue] next() moving: {} -> {}", current, next_index);
        } else {
            log::debug!("[Queue] next() repeat one, staying at index {}", current);
        }

        self.current_index = Some(next_index);
        self.items.get(next_index)
    }

    /// Move to the previous item
    pub fn previous(&mut self) -> Option<&MediaItem> {
        if self.items.is_empty() {
            return None;
        }

        let current = self.current_index?;

        // If we have history, go back in history
        // But validate it to prevent wraparound bugs
        if let Some(prev) = self.history.pop() {
            // Safety check: ensure the history entry is valid
            if prev >= self.items.len() {
                log::warn!(
                    "[Queue] Invalid history entry {} (queue has {} items), clearing history",
                    prev,
                    self.items.len()
                );
                self.history.clear();
                return None;
            }

            // In non-shuffle mode, previous track should be before current (or this is from a skip_to)
            // This prevents going from first track to last track
            if !self.shuffle && prev >= current {
                log::warn!("[Queue] Suspicious history: going from index {} to {} (non-shuffle mode), clearing history",
                    current, prev);
                self.history.clear();
                return None;
            }

            log::debug!("[Queue] previous() using history: {} -> {}", current, prev);
            self.current_index = Some(prev);
            return self.items.get(prev);
        }

        log::debug!("[Queue] previous() no history, current={}", current);

        let prev_index = if self.shuffle {
            // In shuffle mode without history, go to previous in shuffle order
            let pos = self.shuffle_order.iter().position(|&i| i == current)?;
            if pos > 0 {
                self.shuffle_order[pos - 1]
            } else {
                log::debug!("[Queue] previous() at start of shuffle order, staying at current");
                return None;
            }
        } else {
            // Normal sequential order
            if current > 0 {
                current - 1
            } else {
                log::debug!("[Queue] previous() at index 0, staying at current");
                return None;
            }
        };

        log::debug!("[Queue] previous() moving: {} -> {}", current, prev_index);
        self.current_index = Some(prev_index);
        self.items.get(prev_index)
    }

    /// Skip to a specific index
    pub fn skip_to(&mut self, index: usize) -> Option<&MediaItem> {
        if index >= self.items.len() {
            return None;
        }

        if let Some(current) = self.current_index {
            self.history.push(current);
        }

        self.current_index = Some(index);
        self.items.get(index)
    }

    /// Toggle shuffle mode
    pub fn toggle_shuffle(&mut self) {
        self.shuffle = !self.shuffle;

        if self.shuffle && !self.items.is_empty() {
            self.shuffle_order = self.generate_shuffle_order(self.items.len(), self.current_index);
        } else {
            self.shuffle_order.clear();
        }
    }

    /// Cycle through repeat modes
    pub fn cycle_repeat(&mut self) {
        self.repeat = match self.repeat {
            RepeatMode::Off => RepeatMode::All,
            RepeatMode::All => RepeatMode::One,
            RepeatMode::One => RepeatMode::Off,
        };
    }

    /// Set repeat mode directly (for testing)
    #[cfg(test)]
    pub fn set_repeat(&mut self, mode: RepeatMode) {
        self.repeat = mode;
    }

    /// Check if there's a next item available
    pub fn has_next(&self) -> bool {
        if self.items.is_empty() {
            return false;
        }

        match self.current_index {
            None => false,
            Some(current) => {
                if self.repeat == RepeatMode::All || self.repeat == RepeatMode::One {
                    true
                } else if self.shuffle {
                    let pos = self.shuffle_order.iter().position(|&i| i == current);
                    pos.map(|p| p + 1 < self.shuffle_order.len())
                        .unwrap_or(false)
                } else {
                    current + 1 < self.items.len()
                }
            }
        }
    }

    /// Check if there's a previous item available
    pub fn has_previous(&self) -> bool {
        !self.history.is_empty() || {
            match self.current_index {
                None => false,
                Some(current) => {
                    if self.shuffle {
                        let pos = self.shuffle_order.iter().position(|&i| i == current);
                        pos.map(|p| p > 0).unwrap_or(false)
                    } else {
                        current > 0
                    }
                }
            }
        }
    }

    /// Get the next N upcoming items (for preloading)
    /// Returns items that will play after the current item, respecting shuffle order
    pub fn get_upcoming(&self, count: usize) -> Vec<&MediaItem> {
        if self.items.is_empty() || count == 0 {
            return Vec::new();
        }

        let current = match self.current_index {
            Some(idx) => idx,
            None => return Vec::new(),
        };

        let mut upcoming = Vec::with_capacity(count);

        if self.shuffle {
            // Find current position in shuffle order
            if let Some(pos) = self.shuffle_order.iter().position(|&i| i == current) {
                for i in 1..=count {
                    let next_pos = pos + i;
                    if next_pos < self.shuffle_order.len() {
                        if let Some(item) = self.items.get(self.shuffle_order[next_pos]) {
                            upcoming.push(item);
                        }
                    } else if self.repeat == RepeatMode::All {
                        // Wrap around if repeat all is enabled
                        let wrapped_pos = (next_pos) % self.shuffle_order.len();
                        if let Some(item) = self.items.get(self.shuffle_order[wrapped_pos]) {
                            upcoming.push(item);
                        }
                    }
                }
            }
        } else {
            // Normal sequential order
            for i in 1..=count {
                let next_idx = current + i;
                if next_idx < self.items.len() {
                    upcoming.push(&self.items[next_idx]);
                } else if self.repeat == RepeatMode::All {
                    // Wrap around if repeat all is enabled
                    let wrapped_idx = next_idx % self.items.len();
                    upcoming.push(&self.items[wrapped_idx]);
                }
            }
        }

        upcoming
    }

    /// Move an item from one index to another
    pub fn move_item(&mut self, from_index: usize, to_index: usize) -> bool {
        if from_index >= self.items.len() || to_index >= self.items.len() || from_index == to_index
        {
            return false;
        }

        // Remove the item and insert at new position
        let item = self.items.remove(from_index);
        self.items.insert(to_index, item);

        // Update current_index if affected
        if let Some(current) = self.current_index {
            if current == from_index {
                // The moved item was the current one
                self.current_index = Some(to_index);
            } else if from_index < current && to_index >= current {
                // Item moved from before current to after/at current
                self.current_index = Some(current - 1);
            } else if from_index > current && to_index <= current {
                // Item moved from after current to before/at current
                self.current_index = Some(current + 1);
            }
        }

        // Update shuffle order if shuffle is on
        if self.shuffle && !self.shuffle_order.is_empty() {
            // Regenerate shuffle order to maintain consistency
            self.shuffle_order = self.generate_shuffle_order(self.items.len(), self.current_index);
        }

        true
    }

    /// Update the stream URL of the current item (for transcoded seeking)
    /// Returns true if the update was successful
    pub fn update_current_stream_url(&mut self, new_url: String) -> bool {
        if let Some(current_index) = self.current_index {
            if let Some(item) = self.items.get_mut(current_index) {
                // Only update if it's a Remote source
                if let MediaSource::Remote {
                    jellyfin_item_id, ..
                } = &item.source
                {
                    item.source = MediaSource::Remote {
                        stream_url: new_url,
                        jellyfin_item_id: jellyfin_item_id.clone(),
                    };
                    return true;
                }
            }
        }
        false
    }

    /// Generate a shuffle order, optionally starting from a specific index
    fn generate_shuffle_order(&self, length: usize, start_index: Option<usize>) -> Vec<usize> {
        let mut indices: Vec<usize> = (0..length).collect();
        let mut rng = rand::thread_rng();
        indices.shuffle(&mut rng);

        // Move start index to the front if specified
        if let Some(start) = start_index {
            if let Some(pos) = indices.iter().position(|&i| i == start) {
                indices.remove(pos);
                indices.insert(0, start);
            }
        }

        indices
    }
}

/// Position to add items to the queue
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddPosition {
    /// Add immediately after current item
    Next,
    /// Add at the end of the queue
    End,
}

// TRACES: UR-005, UR-015 | DR-005 | UT-003, UT-004, UT-005
#[cfg(test)]
mod tests {
    use super::*;
    use crate::player::media::{MediaSource, MediaType};

    fn create_test_items(count: usize) -> Vec<MediaItem> {
        (0..count)
            .map(|i| MediaItem {
                id: format!("item_{}", i),
                title: format!("Track {}", i + 1),
                name: Some(format!("Track {}", i + 1)),
                artist: Some("Artist".to_string()),
                album: None,
                album_name: None,
                album_id: None,
                artist_items: None,
                artists: Some(vec!["Artist".to_string()]),
                primary_image_tag: None,
                image_id: None,
                item_type: Some("Audio".to_string()),
                playlist_id: None,
                duration: Some(180.0),
                artwork_url: None,
                media_type: MediaType::Audio,
                source: MediaSource::DirectUrl {
                    url: format!("http://example.com/track_{}.mp3", i),
                },
                video_codec: None,
                needs_transcoding: false,
                video_width: None,
                video_height: None,
                subtitles: vec![],
                series_id: None,
                server_id: None,
            })
            .collect()
    }

    /// Test setting up the queue with items
    ///
    /// @req-test: UR-015 - View and manage audio queue (add tracks)
    /// @req-test: DR-005 - Queue manager with shuffle, repeat, history
    #[test]
    fn test_set_queue() {
        let mut queue = QueueManager::new();
        let items = create_test_items(5);

        queue.set_queue(items.clone(), 0);

        assert_eq!(queue.items().len(), 5);
        assert_eq!(queue.current_index(), Some(0));
        assert_eq!(queue.current().unwrap().id, "item_0");
    }

    /// Test clearing the queue returns it to the empty state so the frontend
    /// hides the mini player on a genuine stop.
    #[test]
    fn test_clear() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 1);
        assert_eq!(queue.current_index(), Some(1));

        queue.clear();

        assert_eq!(queue.items().len(), 0);
        assert_eq!(queue.current_index(), None);
        assert!(queue.current().is_none());
    }

    /// Test next track navigation
    ///
    /// @req-test: UR-005 - Control media playback (skip to next track)
    /// @req-test: DR-005 - Queue manager with shuffle, repeat, history
    #[test]
    fn test_next() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 0);

        assert_eq!(queue.current().unwrap().id, "item_0");

        queue.next();
        assert_eq!(queue.current().unwrap().id, "item_1");

        queue.next();
        assert_eq!(queue.current().unwrap().id, "item_2");

        // No next without repeat
        assert!(queue.next().is_none());
    }

    /// Test repeat all mode wraps to beginning
    ///
    /// @req-test: UR-005 - Control media playback (repeat all mode)
    /// @req-test: DR-005 - Queue manager with repeat
    #[test]
    fn test_repeat_all() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(2), 0);
        queue.set_repeat(RepeatMode::All);

        queue.next(); // Move to item_1
        let next = queue.next(); // Should wrap to item_0

        assert!(next.is_some());
        assert_eq!(queue.current().unwrap().id, "item_0");
    }

    /// Test previous track navigation
    ///
    /// @req-test: UR-005 - Control media playback (previous track)
    /// @req-test: DR-005 - Queue manager
    #[test]
    fn test_previous() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 2);

        queue.previous();
        assert_eq!(queue.current_index(), Some(1));
    }

    /// Test viewing upcoming tracks in queue
    ///
    /// @req-test: UR-015 - View and manage audio queue
    /// @req-test: DR-020 - Queue management UI (upcoming tracks)
    #[test]
    fn test_get_upcoming() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 0);

        // Get next 3 items
        let upcoming = queue.get_upcoming(3);
        assert_eq!(upcoming.len(), 3);
        assert_eq!(upcoming[0].id, "item_1");
        assert_eq!(upcoming[1].id, "item_2");
        assert_eq!(upcoming[2].id, "item_3");

        // Move to item 2 and get upcoming
        queue.next();
        queue.next();
        let upcoming = queue.get_upcoming(3);
        assert_eq!(upcoming.len(), 2); // Only 2 items remaining
        assert_eq!(upcoming[0].id, "item_3");
        assert_eq!(upcoming[1].id, "item_4");
    }

    /// Test upcoming tracks with repeat all mode
    ///
    /// @req-test: UR-015 - View and manage audio queue
    /// @req-test: DR-005 - Queue manager with repeat
    #[test]
    fn test_get_upcoming_with_repeat() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 1);
        queue.set_repeat(RepeatMode::All);

        // At item_1, get upcoming with repeat
        let upcoming = queue.get_upcoming(4);
        assert_eq!(upcoming.len(), 4);
        assert_eq!(upcoming[0].id, "item_2"); // Next
        assert_eq!(upcoming[1].id, "item_0"); // Wrapped
        assert_eq!(upcoming[2].id, "item_1"); // Wrapped (current again)
        assert_eq!(upcoming[3].id, "item_2"); // Wrapped
    }

    /// Test upcoming tracks on empty queue
    ///
    /// @req-test: DR-005 - Queue manager (edge case: empty queue)
    #[test]
    fn test_get_upcoming_empty() {
        let queue = QueueManager::new();
        let upcoming = queue.get_upcoming(3);
        assert!(upcoming.is_empty());
    }

    /// Test next stops at end without repeat mode
    ///
    /// @req-test: UR-005 - Control media playback (queue end behavior)
    /// @req-test: DR-005 - Queue manager
    #[test]
    fn test_next_no_repeat_reaches_end() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 0);
        queue.set_repeat(RepeatMode::Off);

        // At item 0, should have next
        assert!(queue.has_next());
        assert_eq!(queue.current_index(), Some(0));

        // Move to item 1
        queue.next();
        assert!(queue.has_next());
        assert_eq!(queue.current_index(), Some(1));

        // Move to item 2 (last)
        queue.next();
        assert!(!queue.has_next()); // No more items
        assert_eq!(queue.current_index(), Some(2));

        // Try to move past end
        let result = queue.next();
        assert!(result.is_none());
        assert_eq!(queue.current_index(), Some(2)); // Should stay at last item
    }

    /// Test next wraps to beginning with repeat all
    ///
    /// @req-test: UR-005 - Control media playback (repeat all wrapping)
    /// @req-test: DR-005 - Queue manager with repeat
    #[test]
    fn test_next_repeat_all_wraps() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 0);
        queue.set_repeat(RepeatMode::All);

        // With repeat all, has_next should always be true
        assert!(queue.has_next());

        // Move through all items
        queue.next(); // item_1
        assert!(queue.has_next());
        queue.next(); // item_2
        assert!(queue.has_next());

        // Wrap to beginning
        let result = queue.next();
        assert!(result.is_some());
        assert_eq!(queue.current().unwrap().id, "item_0");
        assert!(queue.has_next()); // Still has next (loops forever)
    }

    /// Test next repeats same track with repeat one mode
    ///
    /// @req-test: UR-005 - Control media playback (repeat one mode)
    /// @req-test: DR-005 - Queue manager with repeat
    #[test]
    fn test_next_repeat_one_stays() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 1);
        queue.set_repeat(RepeatMode::One);

        // Should always have next (repeats current)
        assert!(queue.has_next());
        assert_eq!(queue.current_index(), Some(1));

        // Call next multiple times - should stay on same track
        for _ in 0..5 {
            let result = queue.next();
            assert!(result.is_some());
            assert_eq!(queue.current().unwrap().id, "item_1");
            assert_eq!(queue.current_index(), Some(1));
            assert!(queue.has_next());
        }
    }

    /// Test shuffle mode follows randomized order
    ///
    /// @req-test: UR-005 - Control media playback (shuffle mode)
    /// @req-test: DR-005 - Queue manager with shuffle
    #[test]
    fn test_next_shuffle_follows_order() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(4), 0);
        queue.toggle_shuffle(); // Enable shuffle

        // Get the shuffle order for verification
        let shuffle_order = queue.shuffle_order.clone();
        assert_eq!(shuffle_order.len(), 4);

        // Current should be first item in shuffle order
        let first_shuffled_index = shuffle_order[0];
        assert_eq!(queue.current_index(), Some(first_shuffled_index));

        // Move through shuffle order
        for i in 1..shuffle_order.len() {
            assert!(queue.has_next());
            let result = queue.next();
            assert!(result.is_some());
            let expected_index = shuffle_order[i];
            assert_eq!(queue.current_index(), Some(expected_index));
        }

        // At end of shuffle without repeat
        assert!(!queue.has_next());
        let result = queue.next();
        assert!(result.is_none());
    }

    /// Test shuffle with repeat all wraps shuffle order
    ///
    /// @req-test: UR-005 - Control media playback (shuffle + repeat)
    /// @req-test: DR-005 - Queue manager with shuffle and repeat
    #[test]
    fn test_next_shuffle_with_repeat_all() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 0);
        queue.toggle_shuffle(); // Enable shuffle
        queue.set_repeat(RepeatMode::All);

        let shuffle_order = queue.shuffle_order.clone();

        // Move through entire shuffle order
        for _ in 1..shuffle_order.len() {
            queue.next();
        }

        // At end, should wrap to beginning of shuffle order
        assert!(queue.has_next());
        let result = queue.next();
        assert!(result.is_some());
        assert_eq!(queue.current_index(), Some(shuffle_order[0]));
    }

    /// Test has_next logic accuracy across different scenarios
    ///
    /// @req-test: DR-005 - Queue manager (has_next accuracy)
    /// @req-test: UR-015 - View and manage audio queue
    #[test]
    fn test_has_next_accuracy() {
        // Test 1: Empty queue
        let queue = QueueManager::new();
        assert!(!queue.has_next());

        // Test 2: Last track with repeat off
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(2), 1); // Start at last item
        queue.set_repeat(RepeatMode::Off);
        assert!(!queue.has_next());

        // Test 3: Last track with repeat all
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(2), 1); // Start at last item
        queue.set_repeat(RepeatMode::All);
        assert!(queue.has_next()); // Should wrap

        // Test 4: Repeat one mode (always has next)
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(1), 0);
        queue.set_repeat(RepeatMode::One);
        assert!(queue.has_next()); // Repeats forever

        // Test 5: Middle of queue with repeat off
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 1); // Middle item
        queue.set_repeat(RepeatMode::Off);
        assert!(queue.has_next()); // Has item_2 next

        // Test 6: Shuffle at end without repeat
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(3), 0);
        queue.toggle_shuffle(); // Enable shuffle
        queue.set_repeat(RepeatMode::Off);
        // Move to last item in shuffle order
        let shuffle_len = queue.shuffle_order.len();
        for _ in 1..shuffle_len {
            queue.next();
        }
        assert!(!queue.has_next());
    }

    /// Test has_next on empty queue edge case
    ///
    /// @req-test: DR-005 - Queue manager (edge case: empty queue)
    #[test]
    fn test_has_next_empty_queue() {
        let queue = QueueManager::new();
        assert!(!queue.has_next());
        assert_eq!(queue.current_index(), None);
    }

    /// Test that selecting a specific track in an album starts at the correct index
    ///
    /// Reproduces the bug where clicking songs 1-5 always played song 13
    #[test]
    fn test_play_specific_track_from_album() {
        let mut queue = QueueManager::new();
        let items = create_test_items(14); // 14-track album

        // Simulate playing track 0 (first track)
        queue.set_queue(items.clone(), 0);
        assert_eq!(queue.current_index(), Some(0));
        assert_eq!(queue.current().unwrap().id, "item_0");

        // Simulate playing track 3 (fourth track)
        queue.set_queue(items.clone(), 3);
        assert_eq!(queue.current_index(), Some(3));
        assert_eq!(queue.current().unwrap().id, "item_3");

        // Simulate playing track 13 (last track)
        queue.set_queue(items, 13);
        assert_eq!(queue.current_index(), Some(13));
        assert_eq!(queue.current().unwrap().id, "item_13");
    }

    /// Test that next() then previous() returns to the original track
    #[test]
    fn test_next_previous_roundtrip() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 2); // Start at middle track

        assert_eq!(queue.current_index(), Some(2));

        // Go to next track
        queue.next();
        assert_eq!(queue.current_index(), Some(3));

        // Go back - should return to track 2
        queue.previous();
        assert_eq!(queue.current_index(), Some(2));
        assert_eq!(queue.current().unwrap().id, "item_2");
    }

    /// Test that previous() at the first track stays at first track
    #[test]
    fn test_previous_at_first_track_stays() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 0); // Start at first track

        assert_eq!(queue.current_index(), Some(0));

        // Try to go to previous - should stay at 0
        let result = queue.previous();
        assert!(result.is_none());
        assert_eq!(queue.current_index(), Some(0));
        assert_eq!(queue.current().unwrap().id, "item_0");
    }

    /// Test that next() at last track (no repeat) stays at last track
    #[test]
    fn test_next_at_last_track_stays() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 4); // Start at last track

        assert_eq!(queue.current_index(), Some(4));

        // Try to go to next - should return None and stay at 4
        let result = queue.next();
        assert!(result.is_none());
        assert_eq!(queue.current_index(), Some(4));
        assert_eq!(queue.current().unwrap().id, "item_4");
    }

    /// Test history validation prevents invalid wraparound
    #[test]
    fn test_history_validation_prevents_wraparound() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 0);

        // Manually corrupt history to simulate the bug
        // (In the real bug, history would have last track's index)
        queue.history.push(4);

        // Try to go previous - should detect invalid history and clear it
        let result = queue.previous();
        assert!(result.is_none()); // Should not wrap to track 4
        assert_eq!(queue.current_index(), Some(0)); // Should stay at track 0
        assert!(queue.history.is_empty()); // History should be cleared
    }

    /// Test multiple next() calls build correct history
    #[test]
    fn test_multiple_next_builds_history() {
        let mut queue = QueueManager::new();
        queue.set_queue(create_test_items(5), 0);

        // Navigate: 0 -> 1 -> 2 -> 3
        queue.next(); // Now at 1, history=[0]
        queue.next(); // Now at 2, history=[0, 1]
        queue.next(); // Now at 3, history=[0, 1, 2]

        assert_eq!(queue.current_index(), Some(3));

        // Go back through history: 3 -> 2 -> 1 -> 0
        queue.previous();
        assert_eq!(queue.current_index(), Some(2));

        queue.previous();
        assert_eq!(queue.current_index(), Some(1));

        queue.previous();
        assert_eq!(queue.current_index(), Some(0));
    }
}
