using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

using Newtonsoft.Json.Linq;

using Vit.Core.Module.Serialization;
using Vit.Core.Util.ComponentModel.Data;
using Vit.Core.Util.ComponentModel.SsError;
using Vit.Db.Module.Schema;
using Vit.Extensions.Newtonsoft_Extensions;
using Vit.Extensions.Object_Extensions;
using Vit.Extensions.Serialize_Extensions;
using Vit.Linq;
using Vit.Linq.ComponentModel;
using Vit.Linq.FilterRules.ComponentModel;

using Vitorm;

namespace Vit.AutoTemp.DataProvider
{
    public class DataProvider_Vitorm<Model, DbContext> : IDataProvider_Vitorm
        where Model : class
        where DbContext : Vitorm.DbContext
    {

        public string template { get; private set; }
        public bool isTree => pidField != null;
        public TableSchema tableSchema { get; private set; }


        protected string idField => controllerConfig?.idField;
        protected string pidField => controllerConfig?.pidField;
        protected readonly Type entityType;
        protected AutoTempControllerConfig controllerConfig;
        protected readonly Func<DbContext> CreateDbContext;
        protected readonly Func<DbContext, IDbSet<Model>> GetDbSet = (dbContext => dbContext.DbSet<Model>());


        public DataProvider_Vitorm(string template, Func<DbContext> CreateDbContext, TableSchema tableSchema = null, Func<DbContext, IDbSet<Model>> GetDbSet = null, AutoTempControllerConfig controllerConfig = null)
        {
            this.template = template;

            this.CreateDbContext = CreateDbContext;

            if (GetDbSet != null)
                this.GetDbSet = GetDbSet;

            entityType = typeof(Model);

            tableSchema ??= AutoTempHelp.EntityTypeToTableSchema(entityType);
            this.tableSchema = tableSchema;

            controllerConfig ??= AutoTempHelp.BuildControllerConfigByTable(tableSchema);
            this.controllerConfig = controllerConfig;
        }


        #region getConfig
        public virtual ApiReturn<AutoTempControllerConfig> getControllerConfig(object sender)
        {
            var config = Json.ConvertBySerialize<AutoTempControllerConfig>(controllerConfig);
            return config;
        }
        #endregion



        #region getList
        public virtual PageData<Model> GetList(FilterRule filter=null, IEnumerable<OrderField> sort = null, PageInfo page = null)
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var query = dbSet.Query();

            var data = query.ToPageData(filter, sort, page);

            return data;
        }

        public virtual ApiReturn getList(object sender, FilterRule filter, IEnumerable<OrderField> sort, PageInfo page, JObject arg) 
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var query = dbSet.Query();

            var data = query.ToPageData(filter, sort, page);

            var pageData = Json.Deserialize<PageData<JObject>>(Json.Serialize(data));

            #region _childrenCount
            if (isTree)
            {
                pageData.items.ForEach(m =>
                {
                    var id = m[idField];
                    var filter = new FilterRule { field = pidField, @operator = "=", value = id };
                    var count = query.Where(filter).Count();
                    m["_childrenCount"] = count;
                });
            }
            #endregion

            return new ApiReturn<object> { data = pageData };
        }
        #endregion


        #region getModel
        public virtual new ApiReturn<Model> getModel(object sender, string id)
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var entity = dbSet.Get(id);

            return entity;
        }

        ApiReturn IDataProvider.getModel(object sender, string id) => getModel(sender, id);

        #endregion

        #region insert
        public virtual ApiReturn insert(object sender, JObject model)
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var userModel = model.ConvertBySerialize<Model>();

            dbSet.Add(userModel);

            return new ApiReturn<object>(userModel);
        }
        #endregion


        #region update
        public virtual ApiReturn update(object sender, JObject model, string id)
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var userModel = model.ConvertBySerialize(entityType);

            var _id = model[idField].GetValue();
            var entity = dbSet.Get(_id);

            if (entity == null)
            {
                return new SsError
                {
                    errorMessage = "待修改的数据不存在"
                };
            }

            entity.CopyNotNullProrertyFrom(userModel);

            dbSet.Update(entity);

            return new ApiReturn<object> { data = entity };

        }
        #endregion


        #region delete
        public virtual ApiReturn delete(object sender, JObject arg)
        {
            using var dbContext = CreateDbContext();
            var dbSet = GetDbSet(dbContext);

            var id = arg["id"].GetValue();
            var count = dbSet.DeleteByKey(id);

            if (count == 0)
            {
                return new SsError
                {
                    errorMessage = "数据不存在"
                };
            }

            return new ApiReturn();
        }
        #endregion
    }
}
