using System;
using System.Linq;
using System.Reflection;
using LLM.Editor.Data;
using Newtonsoft.Json.Linq;

namespace LLM.Editor.Commands
{
    [Serializable]
    public class SetProjectSettingValueCommand : ICommand
    {
        public string apiClassName;
        public string memberName;
        public object value;

        public CommandOutcome Execute(CommandContext context)
        {
            if (string.IsNullOrEmpty(apiClassName) || string.IsNullOrEmpty(memberName))
            {
                context.ErrorMessage = "Error: 'apiClassName' and 'memberName' are required.";
                return CommandOutcome.Error;
            }

            var type = FindType(apiClassName);
            if (type == null)
            {
                context.ErrorMessage = $"Error: Could not find a class named '{apiClassName}'.";
                return CommandOutcome.Error;
            }

            // Try to set a property
            var property = type.GetProperty(memberName, BindingFlags.Public | BindingFlags.Static);
            if (property != null)
            {
                if (!property.CanWrite)
                {
                    context.ErrorMessage = $"Error: Property '{memberName}' on '{apiClassName}' is read-only.";
                    return CommandOutcome.Error;
                }
                try
                {
                    var token = value is JToken ? (JToken)value : JToken.FromObject(value);
                    var convertedValue = token.ToObject(property.PropertyType);
                    property.SetValue(null, convertedValue);
                    return CommandOutcome.Success;
                }
                catch (Exception e)
                {
                    context.ErrorMessage = $"Error setting property '{memberName}': {e.Message}";
                    return CommandOutcome.Error;
                }
            }

            // Try to set a field
            var field = type.GetField(memberName, BindingFlags.Public | BindingFlags.Static);
            if (field != null)
            {
                if (field.IsInitOnly || field.IsLiteral)
                {
                    context.ErrorMessage = $"Error: Field '{memberName}' on '{apiClassName}' is read-only.";
                    return CommandOutcome.Error;
                }
                try
                {
                    var token = value is JToken ? (JToken)value : JToken.FromObject(value);
                    var convertedValue = token.ToObject(field.FieldType);
                    field.SetValue(null, convertedValue);
                    return CommandOutcome.Success;
                }
                catch (Exception e)
                {
                    context.ErrorMessage = $"Error setting field '{memberName}': {e.Message}";
                    return CommandOutcome.Error;
                }
            }

            context.ErrorMessage = $"Error: Could not find a writable public static member named '{memberName}' on '{apiClassName}'.";
            return CommandOutcome.Error;
        }
        
        private Type FindType(string typeName)
        {
            return AppDomain.CurrentDomain.GetAssemblies()
                .Where(a => a.FullName.StartsWith("UnityEngine"))
                .SelectMany(a => a.GetTypes())
                .FirstOrDefault(t => t.FullName != null && t.FullName.Equals(typeName, StringComparison.OrdinalIgnoreCase));
        }
    }
}
