using NexusShell.Models;

namespace NexusShell.Services;

public sealed class NexusQueueService
{
    private readonly List<NexusMediaItem> _queue = [];
    private int _currentIndex = -1;

    public event EventHandler? QueueChanged;

    public IReadOnlyList<NexusMediaQueueItem> GetQueueItems() =>
        _queue.Select((media, index) => new NexusMediaQueueItem { Media = media, Index = index }).ToList();

    public int Count => _queue.Count;

    public int CurrentIndex => _currentIndex;

    public NexusMediaItem? CurrentItem =>
        _currentIndex >= 0 && _currentIndex < _queue.Count ? _queue[_currentIndex] : null;

    public void SetQueue(IEnumerable<NexusMediaItem> items, int startIndex = 0)
    {
        _queue.Clear();
        _queue.AddRange(items);
        _currentIndex = _queue.Count == 0 ? -1 : Math.Clamp(startIndex, 0, _queue.Count - 1);
        QueueChanged?.Invoke(this, EventArgs.Empty);
    }

    public void Add(NexusMediaItem item)
    {
        _queue.Add(item);
        if (_currentIndex < 0)
        {
            _currentIndex = 0;
        }

        QueueChanged?.Invoke(this, EventArgs.Empty);
    }

    public void RemoveAt(int index)
    {
        if (index < 0 || index >= _queue.Count)
        {
            return;
        }

        _queue.RemoveAt(index);
        if (_queue.Count == 0)
        {
            _currentIndex = -1;
        }
        else if (index < _currentIndex)
        {
            _currentIndex--;
        }
        else if (index == _currentIndex)
        {
            _currentIndex = Math.Min(_currentIndex, _queue.Count - 1);
        }

        QueueChanged?.Invoke(this, EventArgs.Empty);
    }

    public void Move(int fromIndex, int toIndex)
    {
        if (fromIndex < 0 || fromIndex >= _queue.Count || toIndex < 0 || toIndex >= _queue.Count || fromIndex == toIndex)
        {
            return;
        }

        var item = _queue[fromIndex];
        _queue.RemoveAt(fromIndex);
        _queue.Insert(toIndex, item);

        if (_currentIndex == fromIndex)
        {
            _currentIndex = toIndex;
        }
        else if (fromIndex < _currentIndex && toIndex >= _currentIndex)
        {
            _currentIndex--;
        }
        else if (fromIndex > _currentIndex && toIndex <= _currentIndex)
        {
            _currentIndex++;
        }

        QueueChanged?.Invoke(this, EventArgs.Empty);
    }

    public NexusMediaItem? MoveNext(bool shuffle, Random? random = null)
    {
        if (_queue.Count == 0)
        {
            return null;
        }

        if (shuffle && _queue.Count > 1)
        {
            var next = random?.Next(_queue.Count) ?? Random.Shared.Next(_queue.Count);
            _currentIndex = next;
            QueueChanged?.Invoke(this, EventArgs.Empty);
            return _queue[_currentIndex];
        }

        _currentIndex = (_currentIndex + 1) % _queue.Count;
        QueueChanged?.Invoke(this, EventArgs.Empty);
        return _queue[_currentIndex];
    }

    public NexusMediaItem? MovePrevious()
    {
        if (_queue.Count == 0)
        {
            return null;
        }

        _currentIndex = _currentIndex <= 0 ? _queue.Count - 1 : _currentIndex - 1;
        QueueChanged?.Invoke(this, EventArgs.Empty);
        return _queue[_currentIndex];
    }

    public void SetCurrentIndex(int index)
    {
        if (index < 0 || index >= _queue.Count)
        {
            return;
        }

        _currentIndex = index;
        QueueChanged?.Invoke(this, EventArgs.Empty);
    }

    public void Clear()
    {
        _queue.Clear();
        _currentIndex = -1;
        QueueChanged?.Invoke(this, EventArgs.Empty);
    }
}
