using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace StellaOps.Policy;

public sealed class InMemoryPolicySnapshotRepository : IPolicySnapshotRepository
{
    private readonly List<PolicySnapshot> _snapshots = new();
    private readonly SemaphoreSlim _mutex = new(1, 1);

    public async Task AddAsync(PolicySnapshot snapshot, CancellationToken cancellationToken = default)
    {
        if (snapshot is null)
        {
            throw new ArgumentNullException(nameof(snapshot));
        }

        await _mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            _snapshots.Add(snapshot);
            _snapshots.Sort(static (left, right) => left.RevisionNumber.CompareTo(right.RevisionNumber));
        }
        finally
        {
            _mutex.Release();
        }
    }

    public async Task<PolicySnapshot?> GetLatestAsync(CancellationToken cancellationToken = default)
    {
        await _mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            return _snapshots.Count == 0 ? null : _snapshots[^1];
        }
        finally
        {
            _mutex.Release();
        }
    }

    public async Task<IReadOnlyList<PolicySnapshot>> ListAsync(int limit, CancellationToken cancellationToken = default)
    {
        await _mutex.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            IEnumerable<PolicySnapshot> query = _snapshots;
            if (limit > 0)
            {
                query = query.TakeLast(limit);
            }

            return query.ToImmutableArray();
        }
        finally
        {
            _mutex.Release();
        }
    }
}
