using System.Reflection;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    #region EF Core Attribute Detection (reflection-based, no dependency)

    /// <summary>
    /// Detect EF Core [Key] attribute via reflection (no EF Core dependency).
    /// </summary>
    private static bool TryGetEFCoreKey(PropertyInfo property)
    {
        var keyAttr = property.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "KeyAttribute");
        return keyAttr != null;
    }

    /// <summary>
    /// Detect EF Core [ForeignKey("NavigationPropertyName")] attribute via reflection.
    /// </summary>
    private static string? TryGetEFCoreForeignKey(PropertyInfo property)
    {
        var fkAttr = property.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "ForeignKeyAttribute");

        if (fkAttr != null)
        {
            // Get the Name property value (navigation property name)
            var nameProp = fkAttr.GetType().GetProperty("Name");
            return nameProp?.GetValue(fkAttr) as string;
        }

        return null;
    }

    /// <summary>
    /// Detect EF Core [InverseProperty("PropertyName")] attribute via reflection.
    /// </summary>
    private static string? TryGetEFCoreInverseProperty(PropertyInfo property)
    {
        var inversePropAttr = property.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "InversePropertyAttribute");

        if (inversePropAttr != null)
        {
            var propertyProp = inversePropAttr.GetType().GetProperty("Property");
            return propertyProp?.GetValue(inversePropAttr) as string;
        }

        return null;
    }

    /// <summary>
    /// Detect EF Core [Table("name")] or [Table(Name = "name", Schema = "schema")] attribute via reflection.
    /// </summary>
    private static string? TryGetEFCoreTableName(Type type)
    {
        var tableAttr = type.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "TableAttribute");

        if (tableAttr != null)
        {
            // EF Core TableAttribute has "Name" property
            var nameProp = tableAttr.GetType().GetProperty("Name");
            return nameProp?.GetValue(tableAttr) as string;
        }

        return null;
    }

    #endregion

    #region Linq2Db Attribute Detection (reflection-based, no dependency)

    /// <summary>
    /// Detect Linq2Db [PrimaryKey] attribute via reflection (no Linq2Db dependency).
    /// </summary>
    private static bool TryGetLinq2DbPrimaryKey(PropertyInfo property)
    {
        var pkAttr = property.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "PrimaryKeyAttribute");
        return pkAttr != null;
    }

    /// <summary>
    /// Detect Linq2Db [Association] attribute and determine navigation type.
    /// Supports simple ThisKey/OtherKey associations. Expression-based associations are skipped.
    /// </summary>
    private static ToonRelationType? TryGetLinq2DbNavigation(PropertyInfo property)
    {
        var assocAttr = property.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "AssociationAttribute");

        if (assocAttr == null)
            return null;

        // Check if it's expression-based (has QueryExpressionMethod property set)
        var queryExprMethod = assocAttr.GetType().GetProperty("QueryExpressionMethod")?.GetValue(assocAttr) as string;
        if (!string.IsNullOrEmpty(queryExprMethod))
        {
            // Expression-based many-to-many - too complex, skip and fallback to convention
            return null;
        }

        // Simple association with ThisKey/OtherKey
        var thisKey = assocAttr.GetType().GetProperty("ThisKey")?.GetValue(assocAttr) as string;
        var otherKey = assocAttr.GetType().GetProperty("OtherKey")?.GetValue(assocAttr) as string;

        var propertyType = property.PropertyType;

        // Check if it's a collection (OneToMany or ManyToMany)
        if (typeof(System.Collections.IEnumerable).IsAssignableFrom(propertyType) && propertyType != typeof(string))
        {
            // Collection with ThisKey + OtherKey -> could be ManyToMany or OneToMany
            // If both ThisKey and OtherKey are set -> likely ManyToMany
            // If only ThisKey -> OneToMany
            if (!string.IsNullOrEmpty(thisKey) && !string.IsNullOrEmpty(otherKey))
            {
                // Could be ManyToMany, but without junction table info, treat as OneToMany
                return ToonRelationType.OneToMany;
            }
            else
            {
                return ToonRelationType.OneToMany;
            }
        }
        else
        {
            // Single object -> ManyToOne or OneToOne
            // Typically ManyToOne
            return ToonRelationType.ManyToOne;
        }
    }

    /// <summary>
    /// Detect Linq2Db [Table(Name = "name")] attribute via reflection.
    /// </summary>
    private static string? TryGetLinq2DbTableName(Type type)
    {
        var tableAttr = type.GetCustomAttributes()
            .FirstOrDefault(a => a.GetType().Name == "TableAttribute");

        if (tableAttr != null)
        {
            // Linq2Db TableAttribute also has "Name" property
            var nameProp = tableAttr.GetType().GetProperty("Name");
            return nameProp?.GetValue(tableAttr) as string;
        }

        return null;
    }

    #endregion
}
