using System;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using System.Reflection;

namespace LLM.Editor.PoC
{
    /// <summary>
    /// A Unity editor utility to export all current console log entries to a text file.
    /// </summary>
    public static class ConsoleLogExporter
    {
        // --- Internal Unity Types and Methods accessed via Reflection ---
        private static Type _logEntriesType;
        private static MethodInfo _startGettingEntriesMethod;
        private static MethodInfo _endGettingEntriesMethod;
        private static MethodInfo _getEntryInternalMethod;
        private static int _logEntryCount;
        private static object _logEntry;

        // --- Menu Item to Trigger the Export ---
        [MenuItem("Tools/Exporter/Export Console Logs")]
        public static void ExportLogs()
        {
            if (!InitializeReflection())
            {
                Debug.LogError("Failed to initialize reflection for accessing console logs. The Unity Editor API may have changed.");
                return;
            }

            // Prompt user for a save location
            var path = EditorUtility.SaveFilePanel("Save Console Logs", "", "console_logs.txt", "txt");

            if (string.IsNullOrEmpty(path))
            {
                // User cancelled the save dialog
                return;
            }

            try
            {
                // Build the formatted log string
                var formattedLogs = BuildFormattedLogString();

                // Write the string to the chosen file
                File.WriteAllText(path, formattedLogs);

                Debug.Log($"<color=lime>Successfully exported console logs to:</color> <a href=\"{path}\">{path}</a>");
            }
            catch (Exception e)
            {
                Debug.LogError($"Failed to export console logs. Error: {e.Message}");
            }
        }

        /// <summary>
        /// Initializes the reflection members needed to access Unity's internal console log API.
        /// </summary>
        /// <returns>True if initialization was successful, otherwise false.</returns>
        private static bool InitializeReflection()
        {
            // Get the internal LogEntries class
            _logEntriesType = Type.GetType("UnityEditor.LogEntries, UnityEditor.dll");
            if (_logEntriesType == null) return false;

            // Get the necessary static methods from LogEntries
            _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries", BindingFlags.Static | BindingFlags.Public);
            _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Static | BindingFlags.Public);
            _getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public);
        
            // Get the total number of log entries
            var getCountMethod = _logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public);
            if (getCountMethod != null)
            {
                _logEntryCount = (int)getCountMethod.Invoke(null, null);
            }

            // Create an instance of the internal LogEntry class to reuse
            var logEntryType = Type.GetType("UnityEditor.LogEntry, UnityEditor.dll");
            if (logEntryType == null) return false;
        
            _logEntry = Activator.CreateInstance(logEntryType);

            return _startGettingEntriesMethod != null && _endGettingEntriesMethod != null && _getEntryInternalMethod != null;
        }

        /// <summary>
        /// Reads all console entries and formats them into a single, readable string.
        /// </summary>
        /// <returns>A formatted string containing all log data.</returns>
        private static string BuildFormattedLogString()
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("==================================================");
            stringBuilder.AppendLine($" CONSOLE LOGS EXPORTED ON: {DateTime.Now}");
            stringBuilder.AppendLine("==================================================");
            stringBuilder.AppendLine();

            // Safely get all entries
            _startGettingEntriesMethod.Invoke(null, null);

            for (var i = 0; i < _logEntryCount; i++)
            {
                // Retrieve a single log entry by its index
                _getEntryInternalMethod.Invoke(null, new[] { i, _logEntry });

                // Extract message and stack trace using reflection from the LogEntry instance
                var messageField = _logEntry.GetType().GetField("message", BindingFlags.Instance | BindingFlags.Public);
                var stackTraceField = _logEntry.GetType().GetField("stackTrace", BindingFlags.Instance | BindingFlags.Public);
                var modeField = _logEntry.GetType().GetField("mode", BindingFlags.Instance | BindingFlags.Public);

                var message = messageField?.GetValue(_logEntry).ToString();
                var stackTrace = stackTraceField?.GetValue(_logEntry).ToString();
                var mode = (int)(modeField?.GetValue(_logEntry) ?? 0);

                // Append formatted entry to the string builder
                stringBuilder.AppendLine("--------------------------------------------------");
                stringBuilder.AppendLine($"TYPE: {GetLogTypeFromMode(mode)}");
                stringBuilder.AppendLine("--------------------------------------------------");
                stringBuilder.AppendLine(message?.Trim());
                stringBuilder.AppendLine();

                if (string.IsNullOrWhiteSpace(stackTrace)) continue;
                
                stringBuilder.AppendLine("STACK TRACE:");
                stringBuilder.AppendLine(stackTrace.Trim());
                stringBuilder.AppendLine();
            }

            // Clean up
            _endGettingEntriesMethod.Invoke(null, null);

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Converts the internal 'mode' integer from a LogEntry into a human-readable log type.
        /// </summary>
        private static string GetLogTypeFromMode(int mode)
        {
            // These are flags, so we need to check for them with bitwise operations.
            // The key is knowing which bits correspond to which log type.
            // This is based on an analysis of Unity's internal LogEntry class.
            const int errorFlag = 1;
            const int assertFlag = 2;
            const int warningFlag = 4;
            const int logFlag = 8;
            // There are other flags for things like compiler errors, etc.

            if ((mode & errorFlag) != 0) return "Error";
            if ((mode & assertFlag) != 0) return "Assert";
            if ((mode & warningFlag) != 0) return "Warning";
            return (mode & logFlag) != 0 ? "Log" :
                // It's possible for a log entry to have other modes (e.g., from packages)
                "Unknown";
        }
    }
}
