#nullable enable

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

namespace StandardScene.CarTypes
{
    /// <summary>
    /// Coder 字段袋元数据导出。
    /// <para>在 StandardScene 程序集内执行反射，可正确读取 internal Fields 类（如 BasicTrackFields）。</para>
    /// </summary>
    public static class CoderFieldsMetadata
    {
        /// <summary>
        /// 描述单个 Fields 类型的字段清单（含继承链上的 public 字段）。
        /// </summary>
        /// <param name="fieldsType">Fields 字段袋类型</param>
        /// <returns>供 SimpleLite API 序列化的匿名结构；fieldsType 为 null 时返回 null</returns>
        public static object? Describe(Type? fieldsType)
        {
            if (fieldsType == null)
            {
                return null;
            }

            var fieldInfos = CollectPublicFieldInfos(fieldsType);
            return new
            {
                typeName = fieldsType.FullName ?? fieldsType.Name,
                shortName = fieldsType.Name,
                assemblyName = fieldsType.Assembly.GetName().Name ?? "",
                baseTypeName = fieldsType.BaseType?.FullName,
                fields = fieldInfos.Select(fi => new
                {
                    name = fi.Name,
                    typeName = fi.FieldType.FullName ?? fi.FieldType.Name,
                    defaultValue = ReadFieldDefault(fieldsType, fi)
                }).ToArray()
            };
        }

        /// <summary>
        /// 自基类到派生类收集 public 实例字段。
        /// </summary>
        static List<FieldInfo> CollectPublicFieldInfos(Type type)
        {
            var ordered = new List<FieldInfo>();
            var seen = new HashSet<string>(StringComparer.Ordinal);

            var chain = new List<Type>();
            for (var t = type; t != null && t != typeof(object); t = t.BaseType)
            {
                chain.Insert(0, t);
            }

            const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
            foreach (var t in chain)
            {
                foreach (var fi in t.GetFields(flags))
                {
                    if (fi.IsStatic || !seen.Add(fi.Name))
                    {
                        continue;
                    }

                    ordered.Add(fi);
                }
            }

            return ordered;
        }

        static object? ReadFieldDefault(Type fieldsType, FieldInfo fi)
        {
            try
            {
                var instance = Activator.CreateInstance(fieldsType, true);
                return NormalizeDefaultValue(fi.GetValue(instance));
            }
            catch
            {
                return null;
            }
        }

        static object? NormalizeDefaultValue(object? value)
        {
            if (value == null)
            {
                return null;
            }

            return value switch
            {
                string or bool or int or long or short or byte or uint or ulong or float or double or decimal => value,
                _ => value.ToString()
            };
        }
    }
}
