using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

using Vit.AutoTemp.DataProvider;
using Vit.Core.Module.Serialization;
using Vit.Core.Util.XmlComment;
using Vit.Db.Module.Schema;

using static Vit.AutoTemp.AutoTempControllerConfig;

namespace Vit.AutoTemp
{
    public class AutoTempHelp
    {

        #region CreateDataProvider
        public static IDataProvider_Vitorm CreateDataProvider(string template, Type entityType, Func<global::Vitorm.DbContext> CreateDbContext, TableSchema tableSchema = null, AutoTempControllerConfig controllerConfig = null)
        {
            return CreateDataProviderMethod.MakeGenericMethod(entityType).Invoke(null, new object[] { template, CreateDbContext, tableSchema, controllerConfig }) as IDataProvider_Vitorm;
        }

        public static DataProvider_Vitorm<Model, global::Vitorm.DbContext> CreateDataProvider<Model>
            (string template, Func<global::Vitorm.DbContext> CreateDbContext, TableSchema tableSchema = null, AutoTempControllerConfig controllerConfig = null)
            where Model : class
        {
            return new DataProvider_Vitorm<Model, global::Vitorm.DbContext>(template, CreateDbContext: CreateDbContext, tableSchema: tableSchema, controllerConfig: controllerConfig);
        }

        static readonly System.Reflection.MethodInfo CreateDataProviderMethod =
            new Func<string, Func<global::Vitorm.DbContext>, TableSchema, AutoTempControllerConfig, DataProvider_Vitorm<object, global::Vitorm.DbContext>>
            (CreateDataProvider<object>)
            .Method.GetGenericMethodDefinition();

        #endregion


        #region EntityTypeToTableSchema 

        public static TableSchema EntityTypeToTableSchema(Type entityType)
        {
            TableSchema tableSchema = new TableSchema { table_name = entityType.GetCustomAttribute<System.ComponentModel.DataAnnotations.Schema.TableAttribute>()?.Name, columns = new List<ColumnSchema>() };

            using (var xmlMng = new XmlCommentMng())
            {
                xmlMng.AddBin();
                var xmlHelp = xmlMng.GetXmlHelp(entityType);

                foreach (var field in entityType.GetProperties())
                {
                    tableSchema.columns.Add(new ColumnSchema
                    {
                        column_name = field.Name,
                        primary_key = (field.GetCustomAttribute<KeyAttribute>() != null) ? 1 : 0,
                        column_comment = xmlHelp?.Property_GetSummary(field),
                        column_clr_type = field.ReflectedType
                    });
                }
            }
            return tableSchema;
        }
        #endregion


        #region BuildControllerConfigByTable
        public static AutoTempControllerConfig BuildControllerConfigByEntityType(Type entityType)
        {
            return BuildControllerConfigByTable(EntityTypeToTableSchema(entityType));
        }
        public static AutoTempControllerConfig BuildControllerConfigByTable(TableSchema tableInfo)
        {
            Regex autoTempReg = new Regex("\\[\\[AutoTemp\\:[\\s\\S]+?\\]\\]"); //  [[AutoTemp: {  }  ]]
            Regex autoTempFieldReg = new Regex("\\[\\[AutoTemp_Field\\:[\\s\\S]+?\\]\\]"); //  [[AutoTemp_Field: {  }  ]]


            AutoTempControllerConfig controllerConfig = null;


            var fields = BuildFields();

            controllerConfig ??= new();

            controllerConfig.rootPidValue ??= "0";
            controllerConfig.fields ??= fields;
            controllerConfig.idField ??= tableInfo.columns.FirstOrDefault(col => col.primary_key == 1)?.column_name;


            return controllerConfig;


            #region build fields
            List<FieldConfig> BuildFields()
            {
                var fields = new List<AutoTempControllerConfig.FieldConfig>();
         
                foreach (var column in tableInfo.columns)
                {
                    FieldConfig field = null;


                    #region Get from comment
                    string comment = column.column_comment;
                    if(!string.IsNullOrWhiteSpace(comment))
                    {
                        // Ignore
                        if (comment.Contains("[[AutoTemp_Field:Ignore]]") == true)
                            continue;

                        // [[AutoTemp: {  }  ]]
                        foreach (Match item in autoTempReg.Matches(comment))
                        {
                            var strJson = item.Value[11..^2];
                            try
                            {
                                controllerConfig = Json.Deserialize<AutoTempControllerConfig>(strJson);
                            }
                            catch { }
                        }


                        // [[AutoTemp_Field: {  }  ]]
                        foreach (Match item in autoTempFieldReg.Matches(comment))
                        {
                            var strJson = item.Value[17..^2];
                            try
                            {
                                field = Json.Deserialize<FieldConfig>(strJson);
                            }
                            catch { }
                        }
                    }
                    #endregion

                    field ??= new();

                    field.list_config ??= new();
                    field.ig_attr ??= new();

                    var title = field.title;
                    if (string.IsNullOrWhiteSpace(title))
                    {
                        if (!string.IsNullOrWhiteSpace(comment))
                        {
                            title = comment;
                            title = autoTempReg.Replace(title, "");
                            title = autoTempFieldReg.Replace(title, "")?.Trim();
                        }

                        if (string.IsNullOrWhiteSpace(title)) title = column.column_name;
                    }

                    field.title ??= title;
                    field.field ??= column.column_name;

                    field.list_config.title ??= title;
                    field.list_config.width ??= 200;
                    field.list_config.visible ??= true;

                    if (column.primary_key == 1)
                    {
                        field.editable ??= false;
                    }
                    fields.Add(field);
                }
                return fields;
            }
            #endregion


      
        }
        #endregion


        #region static dataProviderMap
        public readonly static SortedDictionary<string, IDataProvider> dataProviderMap = new SortedDictionary<string, IDataProvider>();

        public static void RegisterDataProvider(params IDataProvider[] dataProviders)
        {
            lock (dataProviderMap)
            {
                foreach (var dataProvider in dataProviders)
                {
                    dataProviderMap[dataProvider.template] = dataProvider;
                }
            }
        }


        public static void UnRegisterDataProvider(params IDataProvider[] dataProviders)
        {
            lock (dataProviderMap)
                foreach (var dataProvider in dataProviders)
                {
                    dataProviderMap.Remove(dataProvider.template);
                }
        }
        public static IDataProvider GetDataProvider(string template)
        {
            return dataProviderMap.TryGetValue(template, out var v) ? v : null;
        }

        #endregion
    }
}
