using System.Reflection;
using e_suite.API.Common.models;
using NUnit.Framework;

namespace e_suite.API.Common.UnitTests;

[TestFixture]
public class DtoAutoTester
{
    [Test]
    public void RunTests()
    {
        var modelsList = new List<Type>();

        var assembly = Assembly.GetAssembly(typeof(AddRoleSecurityAccess));

        var allTypes = assembly!.GetTypes().Where( x => x.Namespace == "e_suite.API.Common.models");

        foreach (var type in allTypes)
        {
            RunTestsOnClass(type);
        }
    }

    private static void RunTestsOnClass(Type type)
    {
        if (type.IsAbstract)
            return; //Nothing to test here, as abstract classes do not get created.

        if (type.DeclaringType != null)
            return; //types that have a declaring type are doing something funky, and I don't want to test them directly.

        //Create an instance of the object to play with.
        var instance = Activator.CreateInstance(type);

        var allProperties = type.GetProperties();
        foreach (var property in allProperties)
        {
            if (property.CanWrite)
                TestCanWrite(property, instance);

            if (property.CanRead)
                TestCanRead(property, instance);
        }
    }

    private static void TestCanRead(PropertyInfo property, object? instance)
    {
        try
        {
            var result = property.GetValue(instance);
        }
        catch(Exception ex)
        {
            Assert.Fail(ex.Message);
        }
    }

    private static void TestCanWrite(PropertyInfo property, object? instance)
    {
        var propertyType = property.PropertyType;

        if (propertyType.IsInterface)
        {
            try
            {
                var actualPropertyType = GetTypeForInterface(propertyType);

                var writeValue = Activator.CreateInstance(actualPropertyType!);
                property.SetValue(instance, writeValue, null);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.ToString());
            }
        }
        else if (propertyType != typeof(string))
        {
            try
            {
                var writeValue = Activator.CreateInstance(property.PropertyType);
                property.SetValue(instance, writeValue, null);
            }
            catch (Exception ex)
            {
                Assert.Fail(ex.ToString());
            }
        }
    }

    private static Type? GetTypeForInterface(Type propertyType)
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();

        var alltypes = assemblies
            .SelectMany(s => s.GetTypes()).ToList();

        var actualPropertyType = alltypes
            .FirstOrDefault(p => p.GetInterfaces().Any(x => x == propertyType));
        return actualPropertyType;
    }
}