using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json.Linq;
using SGModule.Common.Helper;
using UnityEngine.Events;

namespace SGModule.ConfigLoader {
    public class ConfigModel<T, TD> : ConfigModel where T : class {
        public ConfigModel(string key) : base(key) {
            DataList = new List<TD>();
        }

        public List<TD> DataList {
            get;
            set;
        }

        public override object Parse(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) {
            return LoadConfigList<T, TD>(dictionary, errorCallback);
        }

        protected override T1 LoadConfigList<T1, TD1>(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) {
            ParseConfig(dictionary, out List<TD> data, errorCallback);
            DataList = data;
            return this as T1;
        }
    }

    public class ConfigModel<T> : ConfigModel {
        public ConfigModel(string key) : base(key) {
        }

        public override object Parse(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) {
            return LoadConfig<T>(dictionary, errorCallback);
        }
    }

    public class ConfigModel {
        protected readonly string Key;
        public bool IsList = false;

        protected ConfigModel(string key) {
            Key = key;
        }

        public virtual object Parse(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) {
            return null;
        }

        protected T LoadConfig<T>(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) {
            if (ParseConfig(dictionary, out T data, errorCallback)) {
                return data;
            }

            return default;
        }

        protected virtual T LoadConfigList<T, TD>(IReadOnlyDictionary<string, object> dictionary,
            UnityAction<string, string> errorCallback = null) where T : class {
            return default;
        }

        protected bool ParseConfig<T>(IReadOnlyDictionary<string, object> dictionary, out T obj,
            UnityAction<string, string> errorCallback = null) {
            obj = default;
            if (!dictionary.TryGetValue(Key, out var data)) {
                errorCallback?.Invoke(Key, "Excel Not find Sheet");
                Log.ConfigLoader.Warning($"Excel Not find Sheet: {Key}");
                return false;
            }

            var config = data.ToString();
            try {
                var tempObj = SerializeHelper.ToObject<T>(config);

                if (tempObj != null) {
#if !GAME_RELEASE
                    CheckFields(Key, tempObj, config, FieldCheckType.Missing);
                    CheckFields(Key, tempObj, config, FieldCheckType.Extra);
#endif
                    obj = tempObj;
                    return true;
                }
            }
            catch (Exception ex) {
                errorCallback?.Invoke(Key, ex.Message);
                // 在此处添加异常处理逻辑，例如日志记录
                Log.ConfigLoader.Error("Error deserializing config data for type " + typeof(T).Name + ": " + ex.Message);
            }

            return false;
        }

        private void CheckFields<T>(string key, T tempObj, string configValue, FieldCheckType checkType) {
            List<string> fields;

            if (tempObj is IEnumerable<object> tempArray) {
                fields = CheckFieldsInArray(tempArray.FirstOrDefault()?.GetType(), configValue, checkType);
            }
            else {
                fields = CheckFields(tempObj.GetType(), configValue, checkType);
            }

            if (fields.Count <= 0) {
                return;
            }

            var fieldType = checkType == FieldCheckType.Missing ? "Missing" : "Extra";
            Log.ConfigLoader.Warning($"Excel Sheet: {key} |  {fieldType} fields in JSON: " + string.Join(", ", fields.Distinct()));
        }

        private static List<string> CheckFieldsInArray(Type objType, string jsonString, FieldCheckType checkType) {
            var fields = new List<string>();
            var jsonArray = JArray.Parse(jsonString);

            foreach (var item in jsonArray) {
                fields.AddRange(CheckFields(objType, item.ToString(), checkType));
            }

            return fields;
        }

        private static List<string> CheckFields(Type objType, string jsonString, FieldCheckType checkType) {
            var resultFields = new List<string>();

            try {
                var jsonObject = SerializeHelper.ToObject<Dictionary<string, object>>(jsonString);
                var fieldNames = objType.GetFields(BindingFlags.Public | BindingFlags.Instance)
                    .Select(f => f.Name)
                    .ToHashSet();

                switch (checkType) {
                    // 检查目标类中存在但 JSON 中缺失的字段
                    case FieldCheckType.Missing:
                        resultFields.AddRange(fieldNames.Where(field => !jsonObject.ContainsKey(field)));
                        break;
                    // 检查 JSON 中存在但目标类中不存在的字段
                    case FieldCheckType.Extra:
                        resultFields.AddRange(jsonObject.Keys.Where(key => !fieldNames.Contains(key)));
                        break;
                    default:
                        throw new ArgumentOutOfRangeException(nameof(checkType), checkType, null);
                }
            }
            catch (Exception ex) {
                Log.ConfigLoader.Error("Error checking fields for type " + objType.Name + ": " + ex.Message);
            }

            return resultFields;
        }

        private enum FieldCheckType {
            Missing, // 检查缺失字段
            Extra // 检查多余字段
        }
    }
}
