﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using MongoDB.Driver;

namespace BookShelf.RepositoryAdaptor.MongoDb
{
    public class MongoDbManager<T, TKey> : IDbManager<T, TKey> where T : IEntity<TKey>
    {
        public Expression Expression { get; private set; }

        public Type ElementType { get; private set; }

        public IQueryProvider Provider { get; private set; }

        public IMongoCollection<T> Collection { get; private set; }

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="MongoDbManager{T, TKey}"/> class.
        /// </summary>
        public MongoDbManager()
            : this(InstanceHelper<TKey>.GetDefaultConnectionString())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MongoDbManager{T, TKey}"/> class.
        /// </summary>
        /// <param name="connectionString">The connection string.</param>
        public MongoDbManager(string connectionString)
        {
            Collection = InstanceHelper<TKey>.GetCollectionFromConnectionString<T>(connectionString);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MongoDbManager{T, TKey}"/> class.
        /// </summary>
        /// <param name="connectionString">The connection string.</param>
        /// <param name="collectionname">The collectionname.</param>
        public MongoDbManager(string connectionString, string collectionname)
        {
            this.Collection = InstanceHelper<TKey>.GetCollectionFromConnectionString<T>(connectionString, collectionname);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MongoDbManager{T, TKey}"/> class.
        /// </summary>
        /// <param name="url">The URL.</param>
        public MongoDbManager(MongoUrl url)
        {
            this.Collection = InstanceHelper<TKey>.GetCollectionFromUrl<T>(url);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MongoDbManager{T, TKey}"/> class.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="collectionname">The collectionname.</param>
        public MongoDbManager(MongoUrl url, string collectionname)
        {
            this.Collection = InstanceHelper<TKey>.GetCollectionFromUrl<T>(url, collectionname);
        }
        #endregion


        public IEnumerator<T> GetEnumerator()
        {
            throw new NotImplementedException();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public T GetEntityById(TKey id)
        {
            throw new NotImplementedException();
        }

        public void Add(T entity)
        {
            Collection.InsertOne(entity);
        }

        public async Task AddAsync(T entity)
        {
            await Collection.InsertOneAsync(entity);
        }

        public void Add(IEnumerable<T> entities)
        {
            Collection.InsertMany(entities);
        }

        public async Task AddAsync(IEnumerable<T> entities)
        {
            await Collection.InsertManyAsync(entities);
        }

        public void Update(T entity)
        {
            var filter = Builders<T>.Filter.Eq(e => e.EntityId, entity.EntityId);
            Collection.ReplaceOne(filter, entity);
        }

        public async Task UpdateAsync(T entity)
        {
            var filter = Builders<T>.Filter.Eq(e => e.EntityId, entity.EntityId);
            await Collection.ReplaceOneAsync(filter, entity);
        }

        public void Update(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                Update(entity);
            }
        }

        public async Task UpdateAsync(IEnumerable<T> entities)
        {
            foreach (var entity in entities)
            {
                await UpdateAsync(entity);
            }
        }

        public void Delete(TKey id)
        {
            var filter = Builders<T>.Filter.Eq(e => e.EntityId, id);
            Collection.DeleteOne(filter);
        }

        public void Delete(T entity)
        {
            var filter = Builders<T>.Filter.Eq(e => e.EntityId, entity.EntityId);
            Collection.DeleteOne(filter);
        }

        public void Delete(Expression<Func<T, bool>> predicate)
        {
            throw new NotImplementedException();
        }

        public void DeleteAll()
        {
            throw new NotImplementedException();
        }

        public long Count()
        {
            var filter = Builders<T>.Filter.Empty;
            return Collection.Count(filter);
        }

        public bool Exists(Expression<Func<T, bool>> predicate)
        {
            throw new NotImplementedException();
        }

        public IDisposable RequestStart()
        {
            throw new NotImplementedException();
        }

        public void RequestDone()
        {
            throw new NotImplementedException();
        }        
    }
}