using System;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace LLM.Editor.Analysis
{
    /// <summary>
    /// Provides detailed, serialized data for a specific component on a GameObject.
    /// </summary>
    public class ComponentDataProvider : IContextProvider
    {
        public object GetContext(Object target, string qualifier)
        {
            if (target is not GameObject go)
            {
                return "Error: Target must be a GameObject to get component data.";
            }

            if (string.IsNullOrEmpty(qualifier))
            {
                return "Error: Component type name (qualifier) is required.";
            }

            var componentType = GetTypeByName(qualifier);
            if (componentType == null)
            {
                return $"Error: Component type '{qualifier}' not found.";
            }

            Component component;

            try
            {
                component = go.GetComponent(componentType);
            }
            catch (Exception exception)
            {
                return $"Error: Failed to get component '{qualifier}': {exception.Message}";
            }
            
            return !component ? $"Error: Component '{qualifier}' not found on GameObject '{go.name}'." :
                // Reuse the generic serialization logic for any component.
                SerializeObject(component);
        }

        private static object SerializeObject(Object unityObject)
        {
            var serializedObject = new SerializedObject(unityObject);
            var propertyIterator = serializedObject.GetIterator();
            var propertyData = new Dictionary<string, object>();

            // Use a do-while loop to ensure the first property is processed
            if (propertyIterator.NextVisible(true))
            {
                do
                {
                    propertyData[propertyIterator.name] = GetValueFromProperty(propertyIterator);
                } while (propertyIterator.NextVisible(false));
            }

            return propertyData;
        }

        private static object GetValueFromProperty(SerializedProperty property)
        {
            // This is a simplified version. A full implementation would handle all property types.
            return property.propertyType switch
            {
                SerializedPropertyType.Integer => property.longValue,
                SerializedPropertyType.Boolean => property.boolValue,
                SerializedPropertyType.Float => property.doubleValue,
                SerializedPropertyType.String => property.stringValue,
                SerializedPropertyType.Color => property.colorValue,
                SerializedPropertyType.ObjectReference => GetObjectReferenceValue(property),
                SerializedPropertyType.Enum => property.enumNames[property.enumValueIndex],
                SerializedPropertyType.Vector2 => property.vector2Value,
                SerializedPropertyType.Vector3 => property.vector3Value,
                SerializedPropertyType.Vector4 => property.vector4Value,
                SerializedPropertyType.Rect => property.rectValue,
                SerializedPropertyType.AnimationCurve => property.animationCurveValue,
                SerializedPropertyType.Bounds => property.boundsValue,
                SerializedPropertyType.Quaternion => property.quaternionValue,
                _ => $"Unsupported Type: {property.propertyType}"
            };
        }
        
        private static object GetObjectReferenceValue(SerializedProperty property)
        {
            var obj = property.objectReferenceValue;
            if (obj == null) return null;
            return new { name = obj.name, id = GetStableIdForObject(obj) };
        }
        
        private static string GetStableIdForObject(Object obj)
        {
            if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out long _))
            {
                if (!string.IsNullOrEmpty(guid) && guid != "00000000000000000000000000000000")
                {
                    return guid;
                }
            }
            return obj.GetInstanceID().ToString();
        }

        private static Type GetTypeByName(string typeName)
        {
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                var type = assembly.GetType(typeName);
                if (type != null) return type;
            }
            return null;
        }
    }
}
