using System.Collections.Generic;
using IntelligentProjectAnalyzer.Editor.DependencyViewer;

namespace IntelligentProjectAnalyzer.Editor.Graphing
{
    public class GraphBuilder
    {
        private readonly DependencyGraph _dependencyGraph;
        private readonly AssetNodeResolver _nodeResolver = new();

        public GraphBuilder(DependencyGraph dependencyGraph)
        {
            _dependencyGraph = dependencyGraph;
        }

        public SerializableGraph BuildFromRoot(string rootAssetGuid)
        {
            var graph = new SerializableGraph();
            var guidToIndexMap = new Dictionary<string, int>();
            var guidsToProcess = new Queue<string>();
            var processedGuids = new HashSet<string>();

            guidsToProcess.Enqueue(rootAssetGuid);
            processedGuids.Add(rootAssetGuid);

            while (guidsToProcess.Count > 0)
            {
                var currentGuid = guidsToProcess.Dequeue();
                
                if (!guidToIndexMap.ContainsKey(currentGuid))
                {
                    var node = AssetNodeResolver.CreateNodeFromGuid(currentGuid);
                    graph.Nodes.Add(node);
                    guidToIndexMap[currentGuid] = graph.Nodes.Count - 1;
                }

                ProcessConnections(currentGuid, guidsToProcess, processedGuids);
            }

            foreach (var node in graph.Nodes)
            {
                var sourceIndex = guidToIndexMap[node.Guid];
                var dependencies = _dependencyGraph.GetDependencies(node.Guid);
                foreach (var dependencyGuid in dependencies)
                {
                    if (guidToIndexMap.TryGetValue(dependencyGuid, out var targetIndex))
                    {
                        graph.Links.Add(new GraphLink { Source = sourceIndex, Target = targetIndex });
                    }
                }
            }

            return graph;
        }

        private void ProcessConnections(string guid, Queue<string> toProcess, HashSet<string> processed)
        {
            EnqueueUnprocessed(_dependencyGraph.GetDependencies(guid), toProcess, processed);
            EnqueueUnprocessed(_dependencyGraph.GetReferences(guid), toProcess, processed);
        }

        private void EnqueueUnprocessed(List<string> targetGuids, Queue<string> toProcess, HashSet<string> processed)
        {
            foreach (var targetGuid in targetGuids)
            {
                if (processed.Contains(targetGuid)) continue;
                toProcess.Enqueue(targetGuid);
                processed.Add(targetGuid);
            }
        }
    }
}
