using System.Collections;
using System.Reflection;

namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Entities;

public class EntityConventionTests
{
    private static readonly Assembly CommonsAssembly = typeof(ZB.MOM.WW.ScadaBridge.Commons.Types.RetryPolicy).Assembly;

    private static IEnumerable<Type> GetEntityTypes() =>
        CommonsAssembly.GetTypes()
            .Where(t => t.IsClass && !t.IsAbstract && t.Namespace != null
                        && t.Namespace.Contains(".Entities."));

    [Fact]
    public void AllEntities_ShouldHaveNoEfAttributes()
    {
        var efAttributeNames = new HashSet<string>
        {
            "KeyAttribute", "ForeignKeyAttribute", "TableAttribute",
            "ColumnAttribute", "RequiredAttribute", "MaxLengthAttribute",
            "StringLengthAttribute", "DatabaseGeneratedAttribute",
            "NotMappedAttribute", "IndexAttribute", "InversePropertyAttribute"
        };

        foreach (var entityType in GetEntityTypes())
        {
            // Check class-level attributes
            var classAttrs = entityType.GetCustomAttributes(true);
            foreach (var attr in classAttrs)
            {
                Assert.DoesNotContain(attr.GetType().Name, efAttributeNames);
            }

            // Check property-level attributes
            foreach (var prop in entityType.GetProperties())
            {
                var propAttrs = prop.GetCustomAttributes(true);
                foreach (var attr in propAttrs)
                {
                    Assert.False(efAttributeNames.Contains(attr.GetType().Name),
                        $"Entity {entityType.Name}.{prop.Name} has EF attribute {attr.GetType().Name}");
                }
            }
        }
    }

    [Fact]
    public void AllTimestampProperties_ShouldBeDateTimeOffset()
    {
        var timestampNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "Timestamp", "DeployedAt", "CompletedAt", "GeneratedAt",
            "ReportTimestamp", "SnapshotTimestamp"
        };

        foreach (var entityType in GetEntityTypes())
        {
            foreach (var prop in entityType.GetProperties())
            {
                if (timestampNames.Contains(prop.Name))
                {
                    var underlyingType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                    Assert.True(underlyingType == typeof(DateTimeOffset),
                        $"{entityType.Name}.{prop.Name} should be DateTimeOffset but is {prop.PropertyType.Name}");
                }
            }
        }
    }

    [Fact]
    public void NavigationProperties_ShouldBeICollection()
    {
        foreach (var entityType in GetEntityTypes())
        {
            foreach (var prop in entityType.GetProperties())
            {
                if (prop.PropertyType.IsGenericType &&
                    typeof(IEnumerable).IsAssignableFrom(prop.PropertyType) &&
                    prop.PropertyType != typeof(string))
                {
                    Assert.True(
                        prop.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>),
                        $"{entityType.Name}.{prop.Name} should be ICollection<T> but is {prop.PropertyType.Name}");
                }
            }
        }
    }
}
