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

namespace LLM.Editor.Analysis
{
    /// <summary>
    /// Provides access to the Unity Editor's console log entries using reflection.
    /// </summary>
    public class ConsoleLogProvider : IContextProvider
    {
        private class ConsoleLogEntry
        {
            public string type;
            public string message;
        }

        // Reflection-based access to internal Unity log types
        private static Type _logEntriesType;
        private static MethodInfo _getCountMethod;
        private static MethodInfo _getEntryMethod;
        private static MethodInfo _startGettingEntriesMethod;
        private static MethodInfo _endGettingEntriesMethod;
        private static object _logEntryInstance;

        static ConsoleLogProvider()
        {
            // Initialize reflection types once
            _logEntriesType = Type.GetType("UnityEditor.LogEntries, UnityEditor.dll");
            if (_logEntriesType == null) return;

            _getCountMethod = _logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public);
            _getEntryMethod = _logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public);
            _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries", BindingFlags.Static | BindingFlags.Public);
            _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Static | BindingFlags.Public);
            
            // The LogEntry class is nested within LogEntries
            var logEntryType = Type.GetType("UnityEditor.LogEntry, UnityEditor.dll");
            if (logEntryType != null)
            {
                _logEntryInstance = Activator.CreateInstance(logEntryType);
            }
        }

        public object GetContext(Object target, string qualifier)
        {
            if (_logEntriesType == null || _getCountMethod == null || _getEntryMethod == null || _logEntryInstance == null)
            {
                return "Error: Could not access Unity's internal console log API via reflection.";
            }

            // Parse the qualifier for filters (e.g., "errors:10")
            var parts = qualifier?.Split(':');
            var filterType = parts is { Length: > 0 } ? parts[0].ToLower() : "all";
            var count = parts is { Length: > 1 } && int.TryParse(parts[1], out var num) ? num : 10;

            var logEntries = new List<ConsoleLogEntry>();
            
            _startGettingEntriesMethod.Invoke(null, null);
            var totalCount = (int)_getCountMethod.Invoke(null, null);
            count = Mathf.Min(count, totalCount);

            for (var i = 0; i < totalCount && logEntries.Count < count; i++)
            {
                var index = totalCount - 1 - i; // Read from the most recent
                _getEntryMethod.Invoke(null, new object[] { index, _logEntryInstance });

                var message = (string)_logEntryInstance.GetType().GetField("message", BindingFlags.Instance | BindingFlags.Public)?.GetValue(_logEntryInstance);
                var mode = (int)_logEntryInstance.GetType().GetField("mode", BindingFlags.Instance | BindingFlags.Public)?.GetValue(_logEntryInstance)!;
                
                var logType = GetLogTypeFromMode(mode);

                if (filterType != "all" && (filterType != "errors" || logType != "Error") &&
                    (filterType != "warnings" || logType != "Warning")) continue;
                if (message != null)
                    logEntries.Add(new ConsoleLogEntry { type = logType, message = message.Trim() });
            }
            _endGettingEntriesMethod.Invoke(null, null);

            return logEntries;
        }

        private static string GetLogTypeFromMode(int mode)
        {
            // These mode values are constants defined internally in UnityEditor.LogEntry
            if ((mode & 1) != 0) return "Error"; // 0x0001
            return (mode & 2) != 0 ? "Warning" : // 0x0002
                "Log";
        }
    }
}