// Generated by @typespec/http-server-csharp
// <auto-generated />
#nullable enable

namespace TypeSpec.Helpers
{
    /// <summary>
    /// Default initializer for mock implementations of business logic interfaces
    /// </summary>
    public class Initializer : IInitializer
    {
        /// <summary>
        /// Instantiate the initializer.  The cache *should* be instantiated using ASP.Net Core's dependency injection
        /// </summary>
        /// <param name="cache"></param>
        public Initializer(IDictionary<Type, object?> cache)
        {
            Cache = cache;
        }

        internal virtual IDictionary<Type, object?> Cache { get; }

        internal object? CacheAndReturn(Type type, object? instance)
        {
            Cache[type] = instance;
            return instance;
        }

        /// <summary>
        /// Initialize an object fo the given type
        /// </summary>
        /// <param name="type"> The type to initialize</param>
        /// <returns>An instance of the given type. Or null if initialization was impossible.</returns>
        public object? Initialize (Type type)
        {
            if (Cache.ContainsKey(type))
            {
                return Cache[type];
            }
            if (type == typeof(string))
            {
                return CacheAndReturn(type, string.Empty);
            }
            if (type == typeof(int))
            {
                return CacheAndReturn(type, 0);
            }
            if (type == typeof(long))
            {
                return CacheAndReturn(type, 0L);
            }
            if (type == typeof(float))
            {
                return CacheAndReturn(type, 0.0f);
            }
            if (type == typeof(double))
            {
                return CacheAndReturn(type, 0.0);
            }
            if (type == typeof(decimal))
            {
                return CacheAndReturn(type, 0.0m);
            }
            if (type == typeof(bool))
            {
                return CacheAndReturn(type, false);
            }
            if (type == typeof(byte))
            {
                return CacheAndReturn(type, (byte)0);
            }
            if (type == typeof(char))
            {
                return CacheAndReturn(type, (char)0);
            }
            if (type == typeof(short))
            {
                return CacheAndReturn(type, (short)0);
            }
            if (type == typeof(uint))
            {
                return CacheAndReturn(type, (uint)0);
            }
            if (type == typeof(ulong))
            {
                return CacheAndReturn(type, (ulong)0);
            }
            if (type == typeof(ushort))
            {
                return CacheAndReturn(type, (ushort)0);
            }
            if (type == typeof(sbyte))
            {
                return CacheAndReturn(type, (sbyte)0);
            }
            if (type == typeof(DateTime))
            {
                return CacheAndReturn(type, DateTime.UtcNow);
            }
            if (type == typeof(DateTimeOffset))
            {
                return CacheAndReturn(type, DateTimeOffset.UtcNow);
            }
            if ( type == typeof(TimeSpan))
            {
                return CacheAndReturn(type, TimeSpan.Zero);
            }
            if (type.IsArray)
            {
                var element = type.GetElementType();
                if (element == null) return null;
                return CacheAndReturn(type, Array.CreateInstance(element, 0));
            }
            if (type.IsGenericType)
            {
                var elementType = type.GetGenericArguments()[0];
                if (elementType == null) return null;
                
                if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>)){
                    return CacheAndReturn(type, Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType)));
                }
                if (type.GetGenericTypeDefinition() == typeof(ISet<>))
                {
                    return CacheAndReturn(type, Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(elementType)));
                }
            }
            if (type.IsClass)
            {
                return InitializeClass(type);
            }
            var genericType = Nullable.GetUnderlyingType(type);
            if ( (genericType != null))
            {
                return Initialize(genericType);
            }
            if (type.IsEnum)
            {
              return CacheAndReturn(type, Enum.GetValues(type).GetValue(0));
            }
            return new object();
        }

        /// <summary>
        /// Initialize an object of the given type
        /// </summary>
        /// <typeparam name="T">The type to initialize</typeparam>
        /// <returns>An instance of the given type</returns>
        public T Initialize<T>() where T: class, new()
        {
            var result = new T();
            var initialized = InitializeClass(typeof(T), result);
            return initialized as T ?? result;
        }

        private object? InitializeClass(Type type, object? instance = null)
        {
            if (Cache.ContainsKey(type))
            {
                instance = Cache[type];
                return instance;
            }

            var result = instance == null ? Activator.CreateInstance(type) : instance;
            foreach (var property in type.GetProperties())
            {
                if (property.CanWrite)
                {
                    var propertyType = property.PropertyType;
                    property.SetValue(result, Initialize(propertyType));
                }
            }

            return CacheAndReturn(type, result);
        }
    }
}
