using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Newtonsoft.Json.Linq;

namespace AssetBank.Editor.SceneSchema.Internal
{
    public static class MonoBehaviourSerializer
    {
        public static IDictionary<string, JToken> Serialize(MonoBehaviour obj)
        {
            var fields = new Dictionary<string, JToken>();
            if (obj == null) return fields;

            var serializedObject = new SerializedObject(obj);
            var property = serializedObject.GetIterator();
            property.NextVisible(true); 

            while (property.NextVisible(false))
            {
                fields[property.name] = ConvertPropertyToJToken(property);
            }

            return fields;
        }

        private static JToken ConvertPropertyToJToken(SerializedProperty prop)
        {
            if (prop.depth > 10) return JValue.CreateNull();

            switch (prop.propertyType)
            {
                // Basic Types
                case SerializedPropertyType.Integer: return new JValue(prop.longValue);
                case SerializedPropertyType.Boolean: return new JValue(prop.boolValue);
                case SerializedPropertyType.Float: return new JValue(prop.doubleValue);
                case SerializedPropertyType.String: return new JValue(prop.stringValue);
                case SerializedPropertyType.Enum: return new JValue(prop.enumDisplayNames[prop.enumValueIndex]);

                // Unity-specific Structs
                case SerializedPropertyType.Color:
                    var c = prop.colorValue;
                    return new JObject { { "r", c.r }, { "g", c.g }, { "b", c.b }, { "a", c.a } };
                case SerializedPropertyType.Vector2:
                    var v2 = prop.vector2Value;
                    return new JObject { { "x", v2.x }, { "y", v2.y } };
                case SerializedPropertyType.Vector3:
                    var v3 = prop.vector3Value;
                    return new JObject { { "x", v3.x }, { "y", v3.y }, { "z", v3.z } };
                case SerializedPropertyType.Vector4:
                    var v4 = prop.vector4Value;
                    return new JObject { { "x", v4.x }, { "y", v4.y }, { "z", v4.z }, { "w", v4.w } };
                case SerializedPropertyType.Quaternion:
                    var q = prop.quaternionValue;
                    return new JObject { { "x", q.x }, { "y", q.y }, { "z", q.z }, { "w", q.w } };
                case SerializedPropertyType.Rect:
                    var r = prop.rectValue;
                    return new JObject { { "x", r.x }, { "y", r.y }, { "width", r.width }, { "height", r.height } };
                case SerializedPropertyType.Bounds:
                    var b = prop.boundsValue;
                    return new JObject { { "center", new JObject { { "x", b.center.x }, { "y", b.center.y }, { "z", b.center.z } } }, { "size", new JObject { { "x", b.size.x }, { "y", b.size.y }, { "z", b.size.z } } } };

                // Reference Types
                case SerializedPropertyType.ObjectReference:
                    return SerializeObjectReference(prop.objectReferenceValue);

                // Arrays and Lists
                case SerializedPropertyType.Generic when prop.isArray:
                    var array = new JArray();
                    for (var i = 0; i < prop.arraySize; i++)
                    {
                        array.Add(ConvertPropertyToJToken(prop.GetArrayElementAtIndex(i)));
                    }
                    return array;

                // Nested Serializable Classes
                case SerializedPropertyType.Generic:
                    var nestedObject = new JObject();
                    var copy = prop.Copy();
                    var end = copy.GetEndProperty();
                    copy.NextVisible(true);
                    while (!SerializedProperty.EqualContents(copy, end))
                    {
                        nestedObject[copy.name] = ConvertPropertyToJToken(copy);
                        copy.NextVisible(false);
                    }
                    return nestedObject;

                default:
                    return new JValue($"Unsupported Type: {prop.propertyType}");
            }
        }

        private static JToken SerializeObjectReference(UnityEngine.Object obj)
        {
            if (obj == null) return JValue.CreateNull();

            var reference = new JObject
            {
                ["type"] = obj.GetType().FullName
            };

            if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _))
            {
                reference["guid"] = guid;
                reference["assetPath"] = AssetDatabase.GUIDToAssetPath(guid);
            }
            else
            {
                reference["scene_object_id"] = EditorUtilities.GetLocalFileID(obj);
            }

            return reference;
        }
    }
}
