using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Configuration;
using System.Collections;
using MongoDB.Driver;
using MongoDB.Bson;

namespace DapperORM
{
    /// <summary>
    /// mongodb
    /// </summary>
    public class MongoRepository
    {
        /// <summary>
        /// mongoDB连接配置
        /// </summary>
        private static IDictionary MongoConfig;

        /// <summary>
        /// 客户端对象
        /// </summary>
        protected static MongoClient Client;

        /// <summary>
        /// 数据库
        /// </summary>
        protected static IMongoDatabase Db;

        /// <summary>
        /// 服务器
        /// </summary>
        private static string Servers
        {
            get
            {
                return MongoConfig["serverList"] == null ? null : MongoConfig["serverList"].ToString();
            }
        }

        /// <summary>
        /// 数据库名称
        /// </summary>
        private static string DBName
        {
            get
            {
                return MongoConfig["dbName"] == null ? null : MongoConfig["dbName"].ToString();
            }
        }

        /// <summary>
        /// 登录名
        /// </summary>
        private static string UserId
        {
            get
            {
                return MongoConfig["userId"] == null ? null : MongoConfig["userId"].ToString();
            }
        }

        /// <summary>
        /// 登录密码
        /// </summary>
        private static string Password
        {
            get
            {
                return MongoConfig["password"] == null ? null : MongoConfig["password"].ToString();
            }
        }

        /// <summary>
        /// 副本集名称
        /// </summary>
        private static string ReplicaSetName
        {
            get
            {
                return MongoConfig["replicaSetName"] == null ? null : MongoConfig["replicaSetName"].ToString();
            }
        }

        /// <summary>
        /// 标准连接字符串
        /// </summary>
        private static string ConnStr
        {
            get
            {
                string connStr = "mongodb://";

                if (!string.IsNullOrEmpty(UserId) && !string.IsNullOrEmpty(Password))
                {
                    connStr = string.Concat(connStr, UserId, ":", Password, "@");
                }

                connStr = string.Concat(connStr, Servers, "/", DBName);

                if (!string.IsNullOrEmpty(ReplicaSetName))
                {
                    connStr = string.Concat(connStr, "?slaveOk=true;", "replicaSet=", ReplicaSetName);
                }

                return connStr;
            }
        }

        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        public MongoRepository()
        {
            if (MongoConfig == null)
            {
                MongoConfig = (IDictionary)ConfigurationManager.GetSection("MongoDb");
            }
            if (Client == null)
            {
                MongoUrl url = new MongoUrl(ConnStr);
                Client = new MongoClient(url);
            }
            if (Db == null)
            {
                Db = Client.GetDatabase(DBName);
            }
        }
        #endregion

        #region 插入数据
        /// <summary>
        /// 插入单条数据
        /// </summary>
        /// <param name="collectionName">collection名称</param>
        /// <param name="bson">bson文档</param>
        public void Insert(string collectionName, BsonDocument bson)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            collection.InsertOne(bson, new InsertOneOptions { BypassDocumentValidation = false });
        }

        /// <summary>
        /// 插入多条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="bsonList">数据集合</param>
        public void InsertMany(string collectionName, List<BsonDocument> bsonList)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            collection.InsertManyAsync(bsonList, new InsertManyOptions { BypassDocumentValidation = false });
        }

        /// <summary>
        /// 插入单条数据（泛型）
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="obj">对象</param>
        public void Insert<T>(string collectionName, T obj) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            collection.InsertOne(obj);
        }

        /// <summary>
        /// 插入多条数据（泛型）
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="list">实体集合</param>
        public void InsertMany<T>(string collectionName, List<T> list) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            collection.InsertManyAsync(list);
        }
        #endregion

        #region 删除数据
        /// <summary>
        /// 删除单条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <returns>删除条数</returns>
        public long Delete(string collectionName, FilterDefinition<BsonDocument> filter)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            var result = collection.DeleteOneAsync(filter).Result;
            return result.DeletedCount;
        }

        /// <summary>
        /// 删除多条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <returns>删除条数</returns>
        public long DeleteMany(string collectionName, FilterDefinition<BsonDocument> filter)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            var result = collection.DeleteManyAsync(filter).Result;
            return result.DeletedCount;
        }

        /// <summary>
        /// 删除数据（泛型）
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <returns>删除条数</returns>
        public long Delete<T>(string collectionName, FilterDefinition<T> filter) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            var result = collection.DeleteOneAsync(filter).Result;
            return result.DeletedCount;
        }

        /// <summary>
        /// 删除多条数据（泛型）
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">删除条数</param>
        /// <returns></returns>
        public long DeleteMany<T>(string collectionName, FilterDefinition<T> filter) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            var result = collection.DeleteManyAsync(filter).Result;
            return result.DeletedCount;
        }
        #endregion

        #region 更新数据
        /// <summary>
        /// 更新单条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="update">更新内容</param>
        /// <returns>更新条数</returns>
        public long Update(string collectionName, FilterDefinition<BsonDocument> filter, UpdateDefinition<BsonDocument> update, UpdateOptions updateOptions = null)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            var result = collection.UpdateOneAsync(filter, update, updateOptions).Result;
            return result.ModifiedCount;
        }

        /// <summary>
        /// 更新单条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="update">更新内容</param>
        /// <returns>更新条数</returns>
        public long UpdateMany(string collectionName, FilterDefinition<BsonDocument> filter, UpdateDefinition<BsonDocument> update, UpdateOptions updateOptions = null)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            var result = collection.UpdateManyAsync(filter, update, updateOptions).Result;
            return result.ModifiedCount;
        }

        /// <summary>
        /// 更新单条数据（泛型）
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="update">更新内容</param>
        /// <returns>更新条数</returns>
        public long Update<T>(string collectionName, FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions updateOptions = null) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            var result = collection.UpdateOneAsync(filter, update, updateOptions).Result;
            return result.ModifiedCount;
        }

        /// <summary>
        /// 更新多条数据（泛型）
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="update">更新内容</param>
        /// <returns>更新条数</returns>
        public long UpdateMany<T>(string collectionName, FilterDefinition<T> filter, UpdateDefinition<T> update, UpdateOptions updateOptions = null) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            var result = collection.UpdateManyAsync(filter, update, updateOptions).Result;
            return result.ModifiedCount;
        }

        /// <summary>
        /// 替换单条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="bson">文档</param>
        /// <param name="updateOptions">更新选项</param>
        /// <returns></returns>
        public long Replace(string collectionName, FilterDefinition<BsonDocument> filter, BsonDocument bson, UpdateOptions updateOptions = null)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            if (updateOptions != null && updateOptions.IsUpsert)
            {
                BsonValue value;
                if (bson.TryGetValue("_id", out value) && value != null && value != BsonNull.Value)
                {
                    ObjectId _id;
                    if (!ObjectId.TryParse(value.AsString, out _id) || _id != ObjectId.Empty)
                        bson.Set("_id", ObjectId.GenerateNewId());
                }
                else
                    bson.Set("_id", ObjectId.GenerateNewId());
            }
            var result = collection.ReplaceOneAsync(filter, bson, updateOptions).Result;

            if (updateOptions != null && updateOptions.IsUpsert && result.IsAcknowledged)
                return 1;

            return result.ModifiedCount;
        }

        /// <summary>
        /// 替换单条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <param name="obj">对象</param>
        /// <param name="updateOptions">更新选项</param>
        /// <returns></returns>
        public long Replace<T>(string collectionName, FilterDefinition<T> filter, T obj, UpdateOptions updateOptions = null) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            if (updateOptions != null && updateOptions.IsUpsert)
            {
                var _id = obj.GetType().GetProperties().Where(x => x.PropertyType == typeof(ObjectId)).FirstOrDefault();
                if (_id != null && _id.CanWrite)
                {
                    var value = _id.GetValue(obj);
                    if (value == null || (ObjectId)value == ObjectId.Empty)
                        _id.SetValue(obj, ObjectId.GenerateNewId(), null);
                }
            }
            var result = collection.ReplaceOneAsync(filter, obj, updateOptions).Result;

            if (updateOptions != null && updateOptions.IsUpsert && result.IsAcknowledged)
                return 1;

            return result.ModifiedCount;
        }
        #endregion

        #region 查询数据
        /// <summary>
        /// 查询多条数据
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">过滤条件</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="readCount">记录条数</param>
        /// <param name="projection">查询项</param>
        /// <param name="sort">排序</param>
        /// <param name="usePage">是否分页</param>
        /// <returns></returns>
        public Task<List<BsonDocument>> Read(string collectionName, FilterDefinition<BsonDocument> filter, int? pageIndex, int? pageSize, out long readCount, ProjectionDefinition<BsonDocument> projection = null, SortDefinition<BsonDocument> sort = null, bool usePage = true)
        {
            if (filter == null)
                filter = Builders<BsonDocument>.Filter.Empty;

            var collection = Db.GetCollection<BsonDocument>(collectionName);

            if (usePage)
            {
                readCount = collection.CountAsync(filter).Result;

                pageIndex = pageIndex ?? 1;

                pageSize = pageSize ?? 10;

                int skip = (pageIndex.Value - 1) * pageSize.Value;

                if (projection == null)
                {
                    if (sort == null)
                        return collection.Find(filter).Skip(skip).Limit(pageSize).ToListAsync();
                    else
                        return collection.Find(filter).Sort(sort).Skip(skip).Limit(pageSize).ToListAsync();
                }
                else
                {
                    if (sort == null)
                        return collection.Find(filter).Project(projection).Skip(skip).Limit(pageSize).ToListAsync();
                    else
                        return collection.Find(filter).Project(projection).Sort(sort).Skip(skip).Limit(pageSize).ToListAsync();
                }
            }
            else
            {
                readCount = 0;

                if (sort == null)
                    return collection.Find(filter).ToListAsync();
                else
                    return collection.Find(filter).Sort(sort).ToListAsync();
            }
        }

        /// <summary>
        /// 查询单条数据
        /// </summary>
        /// <param name="collectionName">collection名称</param>
        /// <param name="filter">过滤条件</param>
        /// <param name="projection">需要查询的字段，NULL默认查询全部</param>
        /// <param name="sort">排序条件</param>
        /// <returns></returns>
        public Task<BsonDocument> Read(string collectionName, FilterDefinition<BsonDocument> filter, ProjectionDefinition<BsonDocument> projection = null, SortDefinition<BsonDocument> sort = null)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);

            if (projection == null)
            {
                if (sort == null)
                    return collection.Find(filter).FirstOrDefaultAsync();
                else
                    return collection.Find(filter).Sort(sort).FirstOrDefaultAsync();
            }
            else
            {
                if (sort == null)
                    return collection.Find(filter).Project(projection).FirstOrDefaultAsync();
                else
                    return collection.Find(filter).Project(projection).Sort(sort).FirstOrDefaultAsync();
            }
        }

        /// <summary>
        /// 查询多条数据（泛型）,数据库中字段必须与实体对应
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">过滤条件</param>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页大小</param>
        /// <param name="readCount">记录条数</param>
        /// <param name="projection">查询项</param>
        /// <param name="sort">排序</param>
        /// <param name="usePage">是否分页</param>
        /// <returns></returns>
        public Task<List<T>> Read<T>(string collectionName, int? pageIndex, int? pageSize, out long readCount, FilterDefinition<T> filter, ProjectionDefinition<T, T> projection = null, SortDefinition<T> sort = null, bool usePage = true) where T : new()
        {
            if (filter == null)
                filter = Builders<T>.Filter.Empty;

            var collection = Db.GetCollection<T>(collectionName);

            if (usePage)
            {
                readCount = collection.CountAsync(filter).Result;

                pageIndex = pageIndex ?? 1;

                pageSize = pageSize ?? 10;

                int skip = (pageIndex.Value - 1) * pageSize.Value;

                if (projection == null)
                {
                    if (sort == null)
                        return collection.Find(filter).Skip(skip).Limit(pageSize).ToListAsync();
                    else
                        return collection.Find(filter).Sort(sort).Skip(skip).Limit(pageSize).ToListAsync();
                }
                else
                {
                    if (sort == null)
                        return collection.Find(filter).Project(projection).Skip(skip).Limit(pageSize).ToListAsync();
                    else
                        return collection.Find(filter).Project(projection).Sort(sort).Skip(skip).Limit(pageSize).ToListAsync();
                }
            }
            else
            {
                readCount = 0;

                if (sort == null)
                    return collection.Find(filter).ToListAsync();
                else
                    return collection.Find(filter).Sort(sort).ToListAsync();
            }
        }

        /// <summary>
        /// 查询单条数据（泛型），数据库中字段必须与实体对应
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">过滤条件</param>
        /// <param name="projection">需要查询的字段，NULL默认查询全部</param>
        /// <param name="sort">排序条件</param>
        /// <returns></returns>
        public Task<T> Read<T>(string collectionName, FilterDefinition<T> filter, ProjectionDefinition<T, T> projection = null, SortDefinition<T> sort = null) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);

            if (projection == null)
            {
                if (sort == null)
                    return collection.Find(filter).FirstOrDefaultAsync();
                else
                    return collection.Find(filter).Sort(sort).FirstOrDefaultAsync();
            }
            else
            {
                if (sort == null)
                    return collection.Find(filter).Project(projection).FirstOrDefaultAsync();
                else
                    return collection.Find(filter).Project(projection).Sort(sort).FirstOrDefaultAsync();
            }
        }

        /// <summary>
        /// 获取集合总条数
        /// </summary>
        /// <returns></returns>
        public long GetCount(string collectionName)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            return collection.CountAsync(Builders<BsonDocument>.Filter.Empty).Result;
        }

        /// <summary>
        /// 获取集合总条数
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">条件</param>
        /// <returns></returns>
        public long GetCount(string collectionName, FilterDefinition<BsonDocument> filter)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            return collection.CountAsync(filter).Result;
        }

        /// <summary>
        /// 获取集合总条数
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="collectionName">集合名称</param>
        /// <param name="filter">过滤条件</param>
        /// <returns></returns>
        public long GetCount<T>(string collectionName, FilterDefinition<T> filter) where T : new()
        {
            var collection = Db.GetCollection<T>(collectionName);
            return collection.CountAsync(filter).Result;
        }
        #endregion

        #region 聚合
        /// <summary>
        /// 聚合
        /// </summary>
        /// <param name="collectionName">集合名称</param>
        /// <param name="pipeline">管道信息</param>
        /// <returns></returns>
        public List<BsonDocument> Aggregate(string collectionName, PipelineStagePipelineDefinition<BsonDocument, BsonDocument> pipeline)
        {
            var collection = Db.GetCollection<BsonDocument>(collectionName);
            var cursor = collection.Aggregate(pipeline);
            return cursor.ToList();
        }
        #endregion
    }
}
