using System;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using System.Reflection;
using LLM.Editor.Helper;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace LLM.Editor.Analysis
{
    public class GetDataFromPathProvider : IContextProvider
    {
        public object GetContext(Object target, string qualifier)
        {
            if (string.IsNullOrEmpty(qualifier))
            {
                return "Error: A JSON path (qualifier) is required for GetDataFromPath.";
            }

            var path = qualifier.FromJson<PathRequest>();
            if (path?.steps == null || !path.steps.Any())
            {
                return "Error: Invalid path provided.";
            }

            object currentObject = target;
            var currentGameObject = target switch
            {
                GameObject gameObject => gameObject,
                Component component2 => component2.gameObject,
                _ => null
            };

            foreach (var step in path.steps)
            {
                if (currentObject == null)
                {
                    return "Error: Path evaluation failed at a null object.";
                }
                
                Debug.Log($"Object is: {currentObject}. Type: {currentObject.GetType()}");

                currentObject = step.type.ToLower() switch
                {
                    "component" => currentGameObject ? currentGameObject.GetComponent(GetTypeByName(step.name)) : null,
                    "field" => currentObject.GetType().GetField(step.name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(currentObject),
                    "property" => currentObject.GetType().GetProperty(step.name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(currentObject),
                    "child" => ((GameObject)currentObject).transform.Find(step.name)?.gameObject,
                    _ => "Error: Unknown path step type."
                };

                // If we just navigated to a new GameObject, update our reference.
                if (currentObject is GameObject newGo)
                {
                    currentGameObject = newGo;
                }
            }

            if (currentObject is not Component component) return currentObject;
            
            var allComponents = component.GetComponents<Component>()
                .Select(c => c.GetType().FullName)
                .ToList();

            var result = new Dictionary<string, object>
            {
                ["requestedComponentData"] = component,
                ["allComponentsOnGameObject"] = allComponents
            };

            if (component is not MonoBehaviour monoBehaviour || !IsCustomScript(monoBehaviour)) return result;
            
            // Eager loading for custom scripts
            var script = MonoScript.FromMonoBehaviour(monoBehaviour);
            var scriptPath = AssetDatabase.GetAssetPath(script);
            result["sourceCode"] = File.ReadAllText(scriptPath);

            return result;
        }
        
        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;
        }

        private static bool IsCustomScript(Component component)
        {
            if (!component) return false;
            var script = MonoScript.FromMonoBehaviour(component as MonoBehaviour);
            if (!script) return false;
            var path = AssetDatabase.GetAssetPath(script);
            return !string.IsNullOrEmpty(path) && path.StartsWith("Assets/");
        }
    }

    [Serializable]
    public class PathRequest
    {
        public List<PathStep> steps;
    }

    [Serializable]
    public class PathStep
    {
        public string type;
        public string name;
    }
}