namespace TestProject1
{
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;
    using System.Threading;
    using MongoDB.Bson;
    using MongoDB.Bson.Serialization;
    using MongoDB.Driver;
    using MongoDB.Driver.Linq;
    using Moq;

    [ExcludeFromCodeCoverage]
    public static class MongoDBTestExtensions
    {
        private static readonly LinqProvider _linqProvider = LinqProvider.V3;

        public static bool FilterDefinitionEquals<T>(this FilterDefinition<T> def, FilterDefinition<T> other)
        {
            var reg = BsonSerializer.SerializerRegistry;
            var ser = reg.GetSerializer<T>();
            var defStr = def.Render(ser, reg, _linqProvider).ToJson();
            var otherStr = other.Render(ser, reg, _linqProvider).ToJson();
            return defStr == otherStr;
        }

        public static void InitializeMocks(
            out Mock<IMongoClient> mockClient,
            out Mock<IClientSessionHandle> mockSession)
        {
            mockClient = new Mock<IMongoClient>(MockBehavior.Strict);
            mockSession = new Mock<IClientSessionHandle>(MockBehavior.Strict);
        }

        public static bool PipelineDefinitionEquals<TIN, TOUT>(
            this PipelineDefinition<TIN, TOUT> def,
            PipelineDefinition<TIN, TOUT> other)
        {
            var reg = BsonSerializer.SerializerRegistry;
            var ser = reg.GetSerializer<TIN>();
            var defR = def.Render(ser, reg, _linqProvider);
            var otherR = other.Render(ser, reg, _linqProvider);
            if (defR.Documents.Count != otherR.Documents.Count)
                return false;
            for (var i = 0; i < defR.Documents.Count; i++)
                if (!defR.Documents[i].Equals(otherR.Documents[i]))
                    return false;
            return true;
        }

        public static bool ProjectionDefinitionEquals<TIN, TOUT>(
            this ProjectionDefinition<TIN, TOUT> def,
            ProjectionDefinition<TIN, TOUT> other)
        {
            var reg = BsonSerializer.SerializerRegistry;
            var ser = reg.GetSerializer<TIN>();
            var defStr = def.Render(ser, reg, _linqProvider).ToString();
            var otherStr = other.Render(ser, reg, _linqProvider).ToString();
            return defStr == otherStr;
        }

        public static Mock<IAsyncCursor<T>> SetupAsyncCursor<T>(
            Mock<IAsyncCursor<T>> mockAsyncCursor,
            IEnumerable<T> items,
            bool async = true)
        {
            mockAsyncCursor.Setup(_ => _.Current).Returns(items);
            if (async)
                mockAsyncCursor
                    .SetupSequence(_ => _.MoveNextAsync(It.IsAny<CancellationToken>()))
                        .ReturnsAsync(true)
                        .ReturnsAsync(false);
            else
                mockAsyncCursor
                    .SetupSequence(_ => _.MoveNext(It.IsAny<CancellationToken>()))
                        .Returns(true)
                        .Returns(false);
            mockAsyncCursor.Setup(x => x.Dispose());
            return mockAsyncCursor;
        }

        public static void SetupIAsyncCursor<T>(this Mock<IAsyncCursor<T>> mock, IEnumerable<T> enumerable)
            where T : class
        {
            mock.Setup(_ => _.Current).Returns(enumerable);
            mock.SetupSequence(_ => _.MoveNext(It.IsAny<CancellationToken>()))
                    .Returns(true)
                    .Returns(false);
            mock.Setup(x => x.Dispose());
        }

        public static void SetupIQueryable<T>(this Mock<T> mock, IQueryable queryable)
            where T : class, IQueryable
        {
            mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
            mock.Setup(r => r.Provider).Returns(queryable.Provider);
            mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
            mock.Setup(r => r.Expression).Returns(queryable.Expression);
        }

        public static bool SortDefinitionEquals<T>(this SortDefinition<T> def, SortDefinition<T> other)
        {
            var reg = BsonSerializer.SerializerRegistry;
            var ser = reg.GetSerializer<T>();
            var defStr = def.Render(ser, reg, _linqProvider).ToString();
            var otherStr = other.Render(ser, reg, _linqProvider).ToString();
            return defStr == otherStr;
        }

        public static bool UpdateDefinitionEquals<T>(this UpdateDefinition<T> def, UpdateDefinition<T> other)
        {
            var reg = BsonSerializer.SerializerRegistry;
            var ser = reg.GetSerializer<T>();
            var defStr = def.Render(ser, reg, _linqProvider).ToString();
            var otherStr = other.Render(ser, reg, _linqProvider).ToString();
            return defStr == otherStr;
        }
    }
}