using System.Data;
using System.Linq.Expressions;
using System.Reflection;

namespace Tiobon.Core.Common.DB.Dapper.Extensions;

/// <summary>
/// 泛型扩展
/// </summary>
public static class GenericExtension
{

    public static DataTable ToDataTable<T>(this IEnumerable<T> source, Expression<Func<T, object>> columns = null, bool contianKey = true)
    {
        DataTable dtReturn = new DataTable();
        if (source == null) return dtReturn;

        PropertyInfo[] oProps = typeof(T).GetProperties()
            .Where(x => x.PropertyType.Name != "List`1").ToArray();
        if (columns != null)
        {
            string[] columnArray = columns.GetExpressionToArray();
            oProps = oProps.Where(x => columnArray.Contains(x.Name)).ToArray();
        }
        //移除自增主键
        PropertyInfo keyType = oProps.GetKeyProperty();// oProps.GetKeyProperty()?.PropertyType;
        if (!contianKey && keyType != null && (keyType.PropertyType == typeof(int) || keyType.PropertyType == typeof(long)))
        {
            oProps = oProps.Where(x => x.Name != keyType.Name).ToArray();
        }

        foreach (var pi in oProps)
        {
            var colType = pi.PropertyType;

            if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                colType = colType.GetGenericArguments()[0];
            }

            dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
        }
        foreach (var rec in source)
        {
            var dr = dtReturn.NewRow();
            foreach (var pi in oProps)
            {
                dr[pi.Name] = pi.GetValue(rec, null) == null
                    ? DBNull.Value
                    : pi.GetValue
                        (rec, null);
            }
            dtReturn.Rows.Add(dr);
        }
        return dtReturn;
    }
    public static List<T> ToList<T>(this DataTable dt) where T : class, new()
    {
        Type t = typeof(T);
        PropertyInfo[] propertys = t.GetProperties();
        List<T> lst = new List<T>();
        string typeName = string.Empty;

        foreach (DataRow dr in dt.Rows)
        {
            T entity = new T();
            foreach (PropertyInfo pi in propertys)
            {
                typeName = pi.Name;
                if (dt.Columns.Contains(typeName))
                {
                    if (!pi.CanWrite) continue;
                    object value = dr[typeName];
                    if (value == DBNull.Value) continue;
                    if (pi.PropertyType == typeof(string))
                    {
                        pi.SetValue(entity, value.ToString(), null);
                    }
                    else if (pi.PropertyType == typeof(int) || pi.PropertyType == typeof(int?))
                    {
                        pi.SetValue(entity, int.Parse(value.ToString()), null);
                    }
                    else if (pi.PropertyType == typeof(DateTime?) || pi.PropertyType == typeof(DateTime))
                    {
                        pi.SetValue(entity, DateTime.Parse(value.ToString()), null);
                    }
                    else if (pi.PropertyType == typeof(float))
                    {
                        pi.SetValue(entity, float.Parse(value.ToString()), null);
                    }
                    else if (pi.PropertyType == typeof(double))
                    {
                        pi.SetValue(entity, double.Parse(value.ToString()), null);
                    }
                    else
                    {
                        pi.SetValue(entity, value, null);
                    }
                }
            }
            lst.Add(entity);
        }
        return lst;
    }
}
