using System.Collections;
using static AyCode.Core.Helpers.JsonUtilities;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    /// <summary>
    /// Topological sort for types: dependencies appear before dependents.
    /// Handles circular references by ignoring back-edges.
    /// </summary>
    private static List<Type> TopologicalSortTypes(List<Type> types)
    {
        // Build dependency graph
        var graph = new Dictionary<Type, HashSet<Type>>();
        var inDegree = new Dictionary<Type, int>();

        foreach (var type in types)
        {
            if (!graph.ContainsKey(type))
            {
                graph[type] = new HashSet<Type>();
                inDegree[type] = 0;
            }
        }

        // For each type, find its dependencies (types referenced in properties)
        foreach (var type in types)
        {
            var dependencies = GetTypeDependencies(type, types);
            foreach (var dependency in dependencies)
            {
                if (graph.ContainsKey(dependency) && dependency != type) // Ignore self-references
                {
                    // dependency → type edge (dependency must come before type)
                    if (graph[dependency].Add(type))
                    {
                        inDegree[type]++;
                    }
                }
            }
        }

        // Kahn's algorithm for topological sort
        var queue = new Queue<Type>();
        var result = new List<Type>();

        // Start with types that have no dependencies (in-degree = 0)
        foreach (var type in types)
        {
            if (inDegree[type] == 0)
            {
                queue.Enqueue(type);
            }
        }

        while (queue.Count > 0)
        {
            var current = queue.Dequeue();
            result.Add(current);

            // For each type that depends on current
            foreach (var dependent in graph[current])
            {
                inDegree[dependent]--;
                if (inDegree[dependent] == 0)
                {
                    queue.Enqueue(dependent);
                }
            }
        }

        // If there are cycles, add remaining types in alphabetical order
        if (result.Count < types.Count)
        {
            var remaining = types.Where(t => !result.Contains(t)).OrderBy(t => t.Name);
            result.AddRange(remaining);
        }

        return result;
    }

    /// <summary>
    /// Get all type dependencies for a given type (types referenced in properties).
    /// </summary>
    private static HashSet<Type> GetTypeDependencies(Type type, List<Type> availableTypes)
    {
        var dependencies = new HashSet<Type>();

        if (type.IsEnum)
        {
            // Enums have no dependencies
            return dependencies;
        }

        var metadata = GetTypeMetadata(type);

        foreach (var prop in metadata.Properties)
        {
            var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;

            // Check if this property type is in our available types
            if (availableTypes.Contains(propType) && propType != type)
            {
                dependencies.Add(propType);
            }

            // Check for collection element types
            if (typeof(IEnumerable).IsAssignableFrom(propType) && propType != typeof(string))
            {
                var elementType = GetCollectionElementType(propType);
                if (elementType != null && availableTypes.Contains(elementType) && elementType != type)
                {
                    dependencies.Add(elementType);
                }
            }

            // Check for dictionary key/value types
            if (IsDictionaryType(propType, out var keyType, out var valueType))
            {
                if (keyType != null && availableTypes.Contains(keyType) && keyType != type)
                {
                    dependencies.Add(keyType);
                }
                if (valueType != null && availableTypes.Contains(valueType) && valueType != type)
                {
                    dependencies.Add(valueType);
                }
            }
        }

        return dependencies;
    }
}
