using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Newtonsoft.Json;
using AssetBank.Editor.Data;
using System.Collections.Generic;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using AssetBank.Editor.SceneSchema.Internal;

namespace AssetBank.Editor.SceneSchema
{
    public static class SceneParser
    {   
        public static void Process(string scenePath, string savePath, out string output, out string error)
        {
            output = null;
            error = null;
            
            if (string.IsNullOrEmpty(scenePath) || !File.Exists(scenePath))
            {
                error = $"Scene path is invalid or does not exist. Scene path: {scenePath}";
                return;
            }

            var originalScene = SceneManager.GetActiveScene().path;
            
            try
            {
                var scene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
                var sceneData = new SceneData();
                var prefabList = new List<string>();

                var rootGameObjects = scene.GetRootGameObjects();
                foreach (var rootGo in rootGameObjects)
                {
                    var goData = ParseGameObjectRecursive(rootGo, prefabList);
                    if (goData != null)
                    {
                        sceneData.Hierarchy.Add(goData);
                    }
                }
                
                sceneData.PrefabsInScene = prefabList;
                var json = JsonConvert.SerializeObject(sceneData, new JsonSerializerSettings
                {
                    Formatting = Formatting.None,
                    NullValueHandling = NullValueHandling.Ignore,
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
                });
                
                File.WriteAllText(savePath, json);
                output = $"Successfully processed scene '{scenePath}' and saved JSON to '{savePath}'";
            }
            catch (System.Exception e)
            {
                error = $"Failed to process scene '{scenePath}'. Error: \n{e}";
            }
            finally
            {
                if (!string.IsNullOrEmpty(originalScene))
                {
                    EditorSceneManager.OpenScene(originalScene);
                }
            }
        }

        private static GameObjectData ParseGameObjectRecursive(GameObject go, List<string> prefabList)
        {
            if (go == null) return null;

            var goData = new GameObjectData
            {
                Enabled = go.activeSelf,
                Name = go.name,
                Tag = go.tag,
                Layer = go.layer,
                AnchorId = EditorUtilities.GetLocalFileID(go).ToString()
            };

            var isPrefab = PrefabUtility.IsPartOfPrefabInstance(go);
            var isNewlyAdded = isPrefab && PrefabUtility.GetCorrespondingObjectFromSource(go) == null;

            // Case A: Regular GameObject or a new object added to a prefab instance.
            if (!isPrefab || isNewlyAdded)
            {
                foreach (var component in go.GetComponents<Component>())
                {
                    if (component == null) continue;
                    goData.Components.Add(ParseComponent(component));
                }
            }
            // Case B & C: Object is part of a prefab (root or child).
            else
            {
                // Handle prefab root
                if (PrefabUtility.GetNearestPrefabInstanceRoot(go) == go)
                {
                    var prefabAsset = PrefabUtility.GetCorrespondingObjectFromSource(go);
                    if (prefabAsset != null)
                    {
                        var prefabGuid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(prefabAsset));
                        if (!prefabList.Contains(prefabGuid))
                        {
                            prefabList.Add(prefabGuid);
                        }
                        goData.PrefabIndex = prefabList.IndexOf(prefabGuid);
                    }
                }
                
                // Handle added/removed components for this specific GameObject
                goData.AddedComponents = PrefabUtility.GetAddedComponents(go)
                    .Select(c => ParseComponent(c.instanceComponent))
                    .ToList();

                goData.RemovedComponentAnchorIds = PrefabUtility.GetRemovedComponents(go)
                    .Select(c => EditorUtilities.GetLocalFileID(c.assetComponent).ToString())
                    .ToList();
            }

            // Recursively parse children and prune empty nodes
            foreach (Transform child in go.transform)
            {
                var childData = ParseGameObjectRecursive(child.gameObject, prefabList);
                if (childData != null)
                {
                    goData.Children.Add(childData);
                }
            }

            // Pruning logic: If a prefab node has no overrides and no new children, discard it.
            var isOriginalPrefabPart = isPrefab && !isNewlyAdded;
            if (isOriginalPrefabPart)
            {
                var hasOverrides = goData.AddedComponents.Any() || goData.RemovedComponentAnchorIds.Any();
                var hasChildrenWithOverrides = goData.Children.Any();
                var isRoot = goData.PrefabIndex.HasValue;

                // A non-root part of a prefab is empty if it has no overrides of its own and no children with overrides.
                if (!isRoot && !hasOverrides && !hasChildrenWithOverrides)
                {
                    return null;
                }
            }

            return goData;
        }

        private static ComponentData ParseComponent(Component component)
        {
            var componentData = new ComponentData
            {
                Name = component.GetType().Name,
                AnchorId = EditorUtilities.GetLocalFileID(component).ToString()
            };

            if (component is MonoBehaviour monoBehaviour)
            {
                var fullTypeName = monoBehaviour.GetType().FullName;
                if (fullTypeName != null && !fullTypeName.StartsWith("UnityEngine."))
                {
                    componentData.Fields = MonoBehaviourSerializer.Serialize(monoBehaviour);
                }
            }

            return componentData;
        }
    }
}
