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

namespace LLM.Editor.Analysis
{
    public class GetComponentSchemaProvider : IContextProvider
    {
        public class MemberSchema
        {
            public string name;
            public string memberType; // "Property" or "Field"
            public string valueType;
        }

        public object GetContext(Object target, string qualifier)
        {
            if (string.IsNullOrEmpty(qualifier))
            {
                return "Error: A component type name (qualifier) is required.";
            }

            try
            {
                var type = AppDomain.CurrentDomain.GetAssemblies()
                    .SelectMany(assembly => assembly.GetTypes())
                    .FirstOrDefault(t => t.FullName == qualifier && typeof(Component).IsAssignableFrom(t));

                if (type == null)
                {
                    return $"Error: Could not find a Component type named '{qualifier}'.";
                }

                var members = new List<MemberSchema>();

                // 1. Get all public properties with a public setter, excluding obsolete/hidden ones.
                var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(p => p.CanWrite && p.GetSetMethod(true) != null && p.GetSetMethod(true).IsPublic)
                    .Where(p => !p.IsDefined(typeof(ObsoleteAttribute), true))
                    .Where(p => !p.IsDefined(typeof(HideInInspector), true));
                
                members.AddRange(properties.Select(p => new MemberSchema
                {
                    name = p.Name,
                    memberType = "Property",
                    valueType = p.PropertyType.FullName
                }));

                // 2. Get all fields (public and private) to check for serialization attributes.
                var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                
                var serializableFields = fields.Where(f =>
                {
                    if (f.IsDefined(typeof(ObsoleteAttribute), true)) return false;
                    if (f.IsDefined(typeof(HideInInspector), true)) return false;
                    
                    // A field is serialized if it's public OR has the [SerializeField] attribute.
                    return f.IsPublic || f.IsDefined(typeof(SerializeField), true);
                });

                members.AddRange(serializableFields.Select(f => new MemberSchema
                {
                    name = f.Name,
                    memberType = "Field",
                    valueType = f.FieldType.FullName
                }));

                // Return a distinct list, as a property can have a backing field with the same name.
                return members.GroupBy(m => m.name)
                              .Select(g => g.First())
                              .OrderBy(m => m.name)
                              .ToList();
            }
            catch (Exception e)
            {
                return $"Error reflecting on type '{qualifier}': {e.Message}";
            }
        }
    }
}