using System;
using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
namespace Kyoo.Models
{
	/// 
	/// A class given information about a strongly typed configuration.
	/// 
	public class ConfigurationReference
	{
		/// 
		/// The path of the resource (separated by ':')
		/// 
		public string Path { get; }
		/// 
		/// The type of the resource.
		/// 
		public Type Type { get; }
		
		/// 
		/// Create a new  using a given path and type.
		/// This method does not create sub configuration resources. Please see 
		/// 
		/// The path of the resource (separated by ':' or "__")
		/// The type of the resource
		/// 
		public ConfigurationReference(string path, Type type)
		{
			Path = path;
			Type = type;
		}
		/// 
		/// Return the list of configuration reference a type has.
		/// 
		/// 
		/// The base path of the type (separated by ':' or "__". If empty, it will start at root)
		/// 
		/// The type of the object
		/// The list of configuration reference a type has.
		public static IEnumerable CreateReference(string path, [NotNull] Type type)
		{
			if (type == null)
				throw new ArgumentNullException(nameof(type));
			List ret = new()
			{
				new ConfigurationReference(path, type)
			};
			if (!type.IsClass || type.AssemblyQualifiedName?.StartsWith("System") == true)
				return ret;
			Type enumerable = Utility.GetGenericDefinition(type, typeof(IEnumerable<>));
			Type dictionary = Utility.GetGenericDefinition(type, typeof(IDictionary<,>));
			Type dictionaryKey = dictionary?.GetGenericArguments()[0];
			if (dictionary != null && dictionaryKey == typeof(string))
				ret.AddRange(CreateReference($"{path}:{type.Name}:*", dictionary.GetGenericArguments()[1]));
			else if (dictionary != null && dictionaryKey == typeof(int))
				ret.AddRange(CreateReference($"{path}:{type.Name}:", dictionary.GetGenericArguments()[1]));
			else if (enumerable != null)
				ret.AddRange(CreateReference($"{path}:{type.Name}:", enumerable.GetGenericArguments()[0]));
			else
			{
				foreach (PropertyInfo child in type.GetProperties())
					ret.AddRange(CreateReference($"{path}:{child.Name}", child.PropertyType));
			}
			return ret;
		}
		
		/// 
		/// Return the list of configuration reference a type has.
		/// 
		/// 
		/// The base path of the type (separated by ':' or "__". If empty, it will start at root)
		/// 
		/// The type of the object
		/// The list of configuration reference a type has.
		public static IEnumerable CreateReference(string path)
		{
			return CreateReference(path, typeof(T));
		}
		public static ConfigurationReference CreateUntyped(string path)
		{
			return new(path, null);
		}
	}
}