using HuanMeng.DotNetCore.AttributeExtend;

using System.Collections;
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;

namespace HuanMeng.DotNetCore.Utility;


/// <summary>
/// object转数据字典
/// </summary>
public static class ObjectExtensions
{
    /// <summary>
    /// 用于存储每种类型的属性访问器数组的线程安全缓存。
    /// </summary>
    private static readonly ConcurrentDictionary<Type, PropertyAccessor[]> PropertyCache = new();

    /// <summary>
    /// 缓存每个属性是否具有 ImagesAttribute 特性。
    /// </summary>
    public static readonly ConcurrentDictionary<PropertyInfo, bool> _PropertyCache = new();

    /// <summary>
    /// 判断对象是否为原始类型或字符串类型。
    /// </summary>
    /// <param name="obj">要检查的对象。</param>
    /// <returns>如果对象是原始类型或字符串，返回 true；否则返回 false。</returns>
    public static bool IsPrimitiveType(object obj) =>
        obj is not null && (obj.GetType().IsPrimitive || obj is string or ValueType);

    /// <summary>
    /// 判断对象是否为集合类型（不包括字符串）。
    /// </summary>
    /// <param name="obj">要检查的对象。</param>
    /// <returns>如果对象是集合类型（但不是字符串），返回 true；否则返回 false。</returns>
    public static bool IsCollectionType(object obj) => obj is IEnumerable && obj is not string;

    /// <summary>
    /// 根据对象类型，将对象转换为字典或列表格式，支持可选的路径前缀。
    /// </summary>
    /// <param name="obj">要转换的对象。</param>
    /// <param name="prefix">属性路径的可选前缀。</param>
    /// <returns>对象的字典或列表表示形式。</returns>
    public static object ToDictionaryOrList(this object obj, string prefix = "", Func<int, string>? imageFunc = null)
    {
        if (obj == null) return null;

        return obj switch
        {
            _ when IsPrimitiveType(obj) => obj,
            IEnumerable enumerable => TransformCollection(enumerable, prefix, imageFunc),
            _ => TransformObject(obj, prefix, imageFunc)
        };
    }

    /// <summary>
    /// 将集合对象转换为包含转换项的列表，每个项保留其路径前缀。
    /// </summary>
    /// <param name="enumerable">要转换的集合。</param>
    /// <param name="prefix">集合中每个属性路径的前缀。</param>
    /// <returns>转换后的项列表。</returns>
    private static List<object> TransformCollection(IEnumerable enumerable, string prefix = "", Func<int, string>? imageFunc = null)
    {
        var list = new List<object>(enumerable is ICollection collection ? collection.Count : 10);
        int index = 0;
        foreach (var item in enumerable)
        {
            list.Add(ToDictionaryOrList(item, $"{prefix}.[{index}]", imageFunc)); // 为集合中每个项添加路径
            index++;
        }
        return list;
    }

    /// <summary>
    /// 将对象的属性转换为带有路径前缀的字典，并应用前缀规则。
    /// </summary>
    /// <param name="obj">要转换的对象。</param>
    /// <param name="prefix">每个属性路径的前缀。</param>
    /// <returns>包含属性名和属性值的字典。</returns>
    private static Dictionary<string, object> TransformObject(object obj, string prefix = "", Func<int, string>? imageFunc = null)
    {
        if (obj == null)
        {
            return null;
        }
        var type = obj.GetType();
        var accessors = PropertyCache.GetOrAdd(type, CreatePropertyAccessors);
        var keyValuePairs = new Dictionary<string, object>(accessors.Length);

        foreach (var accessor in accessors)
        {
            var propertyPath = $"{prefix}.{accessor.PropertyName}"; // 构建完整的属性路径

            // 使用访问器获取属性值
            var propertyValue = accessor.Getter(obj);

            // 如果属性是字符串，在其值前添加 "test"
            if (propertyValue is string stringValue)
            {
                keyValuePairs[accessor.PropertyName] = stringValue;
                //Console.WriteLine(propertyPath);
                continue;
            }

            // 如果属性具有 ImagesAttribute，在其值前添加 "image"
            // 否则，如果是集合类型，则递归转换
            keyValuePairs[accessor.PropertyName] = accessor.HasImagesAttribute
                ? imageFunc?.Invoke((int)propertyValue) ?? ""
                : ToDictionaryOrList(propertyValue, propertyPath, imageFunc); // IsCollectionType(propertyValue) ?: propertyValue;
        }

        return keyValuePairs;
    }

    /// <summary>
    /// 为给定类型创建属性访问器数组。
    /// </summary>
    /// <param name="type">要创建属性访问器的类型。</param>
    /// <returns>属性访问器数组。</returns>
    private static PropertyAccessor[] CreatePropertyAccessors(Type type)
    {
        var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        return properties.Select(property =>
        {
            // 创建用于访问属性值的委托
            var getter = CreatePropertyGetter(type, property);
            // 检查属性是否具有 ImagesAttribute，并将结果存储在缓存中
            var isImagesAttribute = _PropertyCache.GetOrAdd(property, p => p.GetCustomAttribute<ImagesAttribute>() != null);
            return new PropertyAccessor(property.Name, getter, isImagesAttribute);
        }).ToArray();
    }



    private static Func<object, object> CreatePropertyGetter(Type type, PropertyInfo property)
    {
        var parameter = Expression.Parameter(typeof(object), "obj");
        var castParameter = Expression.Convert(parameter, type);
        var propertyAccess = Expression.Property(castParameter, property);
        var convertPropertyAccess = Expression.Convert(propertyAccess, typeof(object));
        return Expression.Lambda<Func<object, object>>(convertPropertyAccess, parameter).Compile();
    }

    private class PropertyAccessor
    {
        public string PropertyName { get; }
        public Func<object, object> Getter { get; }
        public bool HasImagesAttribute { get; }

        public PropertyAccessor(string propertyName, Func<object, object> getter, bool hasImagesAttribute)
        {
            PropertyName = propertyName;
            Getter = getter;
            HasImagesAttribute = hasImagesAttribute;
        }
    }
}
