using Insight.Infrastructure.Models;
using Microsoft.AspNetCore.Http;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;

namespace Insight.Infrastructure
{
    public static class MongoCollectionExtensions
    {
        private const int _maximumLimit = 100;

        public static async Task<PagedList<TData>> GetPagedAsync<TData>(
            this IMongoCollection<TData> collection,
            FilterDefinition<TData>? filter = null,
            SortDefinition<TData>? sort = null,
            int offset = 0,
            int limit = 10,
            CancellationToken cancellationToken = default)
        {
            if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value > 100");

            var query = collection.Find(filter ?? Builders<TData>.Filter.Empty);

            if (sort is not null) query = query.Sort(sort);

            var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false);
            var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false);

            return new PagedList<TData>(data, offset, limit, total);
        }

        public static async Task<PagedList<TData>> GetPagedAsync<TData>(
            this IMongoCollection<TData> collection,
            HttpRequest request,
            HttpResponse response,
            FilterDefinition<TData>? filter = null,
            SortDefinition<TData>? sort = null,
            int offset = 0,
            int limit = 10,
            CancellationToken cancellationToken = default)
        {
            var result = await GetPagedAsync(collection, filter, sort, offset, limit, cancellationToken).ConfigureAwait(false);

            request?.AddPagination(result);
            response?.AddPagination(result);

            return result;
        }

        public static async Task<PagedList<TResult>> GetPagedAsync<TData, TResult>(
            this IMongoCollection<TData> collection,
            IAggregateFluent<BsonDocument> query,
            int offset = 0,
            int limit = 10,
            CancellationToken cancellationToken = default)
        {
            if (limit > _maximumLimit) throw new InvalidOperationException("invalid limit value");

            var data = await query.Skip(offset).Limit(limit).ToListAsync(cancellationToken).ConfigureAwait(false);
            var total = await collection.EstimatedDocumentCountAsync(null, cancellationToken).ConfigureAwait(false);

            return new PagedList<TResult>(data.Select(x => BsonSerializer.Deserialize<TResult>(x)), offset, limit, total);
        }

        public static async Task<PagedList<TResult>> GetPagedAsync<TData, TResult>(
            this IMongoCollection<TData> collection,
            HttpRequest request,
            HttpResponse response,
            IAggregateFluent<BsonDocument> query,
            int offset = 0,
            int limit = 10,
            CancellationToken cancellationToken = default)
        {
            var result = await GetPagedAsync<TData, TResult>(collection, query, offset, limit, cancellationToken).ConfigureAwait(false);

            request?.AddPagination(result);
            response?.AddPagination(result);

            return result;
        }
    }
}