using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using NLog;

namespace MusicBeePlugin.Clouseau.Introspection
{
    /// <summary>
    /// Utility class for inspecting and manipulating event handlers on controls.
    /// Provides deep introspection into attached event handlers, their targets, and methods.
    /// </summary>
    public class EventInspector
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        private static readonly BindingFlags AllMembers =
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.Static;

        private static readonly BindingFlags PrivateStatic =
            BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.FlattenHierarchy;

        /// <summary>
        /// Gets all event handlers attached to a control, including inherited events.
        /// </summary>
        /// <param name="control">The control to inspect.</param>
        /// <returns>List of event handler information.</returns>
        public List<EventHandlerDetail> GetEventHandlers(Control control)
        {
            var handlers = new List<EventHandlerDetail>();

            if (control == null)
                return handlers;

            try
            {
                // Get the EventHandlerList from Component
                var eventsField = typeof(Component).GetField("events", AllMembers);
                if (eventsField == null)
                {
                    Logger.Warn("Could not find 'events' field on Component");
                    return handlers;
                }

                var eventHandlerList = eventsField.GetValue(control) as EventHandlerList;
                if (eventHandlerList == null)
                {
                    Logger.Debug($"No event handler list found for {control.GetType().Name}");
                    return handlers;
                }

                // Walk through all event keys
                var controlType = control.GetType();
                var allEvents = GetAllEventsWithKeys(controlType);

                foreach (var eventEntry in allEvents)
                {
                    try
                    {
                        var handler = eventHandlerList[eventEntry.Key];
                        if (handler != null)
                        {
                            foreach (var del in handler.GetInvocationList())
                            {
                                handlers.Add(new EventHandlerDetail
                                {
                                    EventName = eventEntry.EventInfo.Name,
                                    EventKey = eventEntry.Key,
                                    HandlerMethod = del.Method,
                                    HandlerMethodName = del.Method?.Name,
                                    DeclaringType = del.Method?.DeclaringType,
                                    DeclaringTypeName = del.Method?.DeclaringType?.FullName,
                                    TargetObject = del.Target,
                                    TargetType = del.Target?.GetType(),
                                    TargetTypeName = del.Target?.GetType()?.FullName ?? "[Static]",
                                    IsPrivate = del.Method?.IsPrivate ?? false,
                                    IsStatic = del.Method?.IsStatic ?? false,
                                    ParameterCount = del.Method?.GetParameters()?.Length ?? 0,
                                    Delegate = del
                                });
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Trace($"Error getting handler for event {eventEntry.EventInfo?.Name}: {ex.Message}");
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"Error inspecting event handlers for {control.GetType().Name}");
            }

            return handlers;
        }

        /// <summary>
        /// Gets all controls with attached event handlers in a control hierarchy.
        /// </summary>
        /// <param name="root">The root control to start from.</param>
        /// <returns>List of controls with their event handlers.</returns>
        public List<ControlEventInfo> GetAllControlsWithHandlers(Control root)
        {
            var results = new List<ControlEventInfo>();

            if (root == null)
                return results;

            CollectControlHandlers(root, results, 0);

            return results;
        }

        private void CollectControlHandlers(Control control, List<ControlEventInfo> results, int depth)
        {
            var handlers = GetEventHandlers(control);

            if (handlers.Count > 0)
            {
                results.Add(new ControlEventInfo
                {
                    Control = control,
                    ControlName = string.IsNullOrEmpty(control.Name) ? $"[{control.GetType().Name}]" : control.Name,
                    ControlType = control.GetType(),
                    ControlTypeName = control.GetType().Name,
                    Depth = depth,
                    EventHandlers = handlers,
                    EventCount = handlers.Count
                });
            }

            foreach (Control child in control.Controls)
            {
                CollectControlHandlers(child, results, depth + 1);
            }
        }

        /// <summary>
        /// Gets detailed information about a specific event handler delegate.
        /// </summary>
        /// <param name="handler">The delegate to inspect.</param>
        /// <returns>Handler information.</returns>
        public EventHandlerDetail GetEventHandlerInfo(Delegate handler)
        {
            if (handler == null)
                return null;

            return new EventHandlerDetail
            {
                HandlerMethod = handler.Method,
                HandlerMethodName = handler.Method?.Name,
                DeclaringType = handler.Method?.DeclaringType,
                DeclaringTypeName = handler.Method?.DeclaringType?.FullName,
                TargetObject = handler.Target,
                TargetType = handler.Target?.GetType(),
                TargetTypeName = handler.Target?.GetType()?.FullName ?? "[Static]",
                IsPrivate = handler.Method?.IsPrivate ?? false,
                IsStatic = handler.Method?.IsStatic ?? false,
                ParameterCount = handler.Method?.GetParameters()?.Length ?? 0,
                Delegate = handler
            };
        }

        /// <summary>
        /// Invokes an event handler on a control.
        /// </summary>
        /// <param name="control">The control to raise the event on.</param>
        /// <param name="eventName">The name of the event.</param>
        /// <param name="args">Optional event arguments (defaults to EventArgs.Empty).</param>
        /// <returns>True if the event was invoked successfully.</returns>
        public bool InvokeEventHandler(Control control, string eventName, EventArgs args = null)
        {
            if (control == null || string.IsNullOrEmpty(eventName))
                return false;

            try
            {
                var handlers = GetEventHandlers(control);
                var targetHandlers = handlers.Where(h =>
                    string.Equals(h.EventName, eventName, StringComparison.OrdinalIgnoreCase)).ToList();

                if (targetHandlers.Count == 0)
                {
                    Logger.Warn($"No handlers found for event '{eventName}' on {control.GetType().Name}");
                    return false;
                }

                foreach (var handler in targetHandlers)
                {
                    InvokeHandler(handler, control, args ?? EventArgs.Empty);
                }

                Logger.Info($"Invoked {targetHandlers.Count} handler(s) for event '{eventName}' on {control.GetType().Name}");
                return true;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"Error invoking event handler '{eventName}' on {control.GetType().Name}");
                return false;
            }
        }

        /// <summary>
        /// Invokes a specific event handler delegate.
        /// </summary>
        /// <param name="handlerDetail">The handler detail to invoke.</param>
        /// <param name="sender">The sender object.</param>
        /// <param name="args">The event arguments.</param>
        /// <returns>True if invoked successfully.</returns>
        public bool InvokeHandler(EventHandlerDetail handlerDetail, object sender, EventArgs args)
        {
            if (handlerDetail?.Delegate == null)
                return false;

            try
            {
                var parameters = handlerDetail.HandlerMethod?.GetParameters();
                object[] invokeArgs;

                if (parameters == null || parameters.Length == 0)
                {
                    invokeArgs = null;
                }
                else if (parameters.Length == 2)
                {
                    invokeArgs = new object[] { sender, args };
                }
                else if (parameters.Length == 1)
                {
                    invokeArgs = new object[] { args };
                }
                else
                {
                    invokeArgs = new object[] { sender, args };
                }

                handlerDetail.Delegate.DynamicInvoke(invokeArgs);
                Logger.Debug($"Successfully invoked handler: {handlerDetail.DeclaringTypeName}.{handlerDetail.HandlerMethodName}");
                return true;
            }
            catch (TargetInvocationException tie)
            {
                Logger.Error(tie.InnerException ?? tie, $"Error in handler: {handlerDetail.HandlerMethodName}");
                throw tie.InnerException ?? tie;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"Error invoking handler: {handlerDetail.HandlerMethodName}");
                throw;
            }
        }

        /// <summary>
        /// Detaches an event handler from a control.
        /// WARNING: This can break functionality. Use with caution.
        /// </summary>
        /// <param name="control">The control to detach from.</param>
        /// <param name="handlerDetail">The handler to detach.</param>
        /// <returns>True if successfully detached.</returns>
        public bool DetachEventHandler(Control control, EventHandlerDetail handlerDetail)
        {
            if (control == null || handlerDetail == null)
                return false;

            try
            {
                var eventsField = typeof(Component).GetField("events", AllMembers);
                if (eventsField == null)
                    return false;

                var eventHandlerList = eventsField.GetValue(control) as EventHandlerList;
                if (eventHandlerList == null)
                    return false;

                if (handlerDetail.EventKey == null)
                    return false;

                // Remove the specific handler
                eventHandlerList.RemoveHandler(handlerDetail.EventKey, handlerDetail.Delegate);

                Logger.Warn($"Detached handler '{handlerDetail.HandlerMethodName}' from event '{handlerDetail.EventName}' on {control.GetType().Name}");
                return true;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, $"Error detaching handler from {control.GetType().Name}");
                return false;
            }
        }

        /// <summary>
        /// Gets all events and their key fields from a control type hierarchy.
        /// </summary>
        private List<EventKeyEntry> GetAllEventsWithKeys(Type controlType)
        {
            var entries = new List<EventKeyEntry>();

            var currentType = controlType;
            while (currentType != null && currentType != typeof(object))
            {
                var events = currentType.GetEvents(AllMembers);

                foreach (var evt in events)
                {
                    // Skip if we already have this event
                    if (entries.Any(e => e.EventInfo.Name == evt.Name))
                        continue;

                    // Try to find the event key field
                    var key = FindEventKey(currentType, evt.Name);

                    if (key != null)
                    {
                        entries.Add(new EventKeyEntry
                        {
                            EventInfo = evt,
                            Key = key,
                            DeclaringType = currentType
                        });
                    }
                }

                currentType = currentType.BaseType;
            }

            return entries;
        }

        /// <summary>
        /// Finds the event key object for an event name.
        /// </summary>
        private object FindEventKey(Type type, string eventName)
        {
            // Common naming patterns for event keys
            var keyPatterns = new[]
            {
                $"Event{eventName}",
                $"EVENT_{eventName.ToUpper()}",
                $"s_{eventName}Event",
                $"_{eventName}Event",
                $"event{eventName}",
                eventName
            };

            foreach (var pattern in keyPatterns)
            {
                var currentType = type;
                while (currentType != null && currentType != typeof(object))
                {
                    var field = currentType.GetField(pattern, PrivateStatic);
                    if (field != null)
                    {
                        try
                        {
                            return field.GetValue(null);
                        }
                        catch
                        {
                            // Continue searching
                        }
                    }
                    currentType = currentType.BaseType;
                }
            }

            return null;
        }

        /// <summary>
        /// Gets event statistics for a control hierarchy.
        /// </summary>
        public EventStatistics GetEventStatistics(Control root)
        {
            var stats = new EventStatistics();
            var allControls = GetAllControlsWithHandlers(root);

            stats.TotalControlsWithHandlers = allControls.Count;
            stats.TotalHandlers = allControls.Sum(c => c.EventCount);

            // Count by event name
            var byEvent = allControls
                .SelectMany(c => c.EventHandlers)
                .GroupBy(h => h.EventName)
                .ToDictionary(g => g.Key, g => g.Count());
            stats.HandlersByEvent = byEvent;

            // Count by target type
            var byTarget = allControls
                .SelectMany(c => c.EventHandlers)
                .GroupBy(h => h.TargetTypeName ?? "Unknown")
                .ToDictionary(g => g.Key, g => g.Count());
            stats.HandlersByTargetType = byTarget;

            // Count by control type
            var byControl = allControls
                .GroupBy(c => c.ControlTypeName)
                .ToDictionary(g => g.Key, g => g.Sum(c => c.EventCount));
            stats.HandlersByControlType = byControl;

            return stats;
        }
    }

    #region Data Models

    /// <summary>
    /// Detailed information about an attached event handler.
    /// </summary>
    public class EventHandlerDetail
    {
        public string EventName { get; set; }
        public object EventKey { get; set; }
        public MethodInfo HandlerMethod { get; set; }
        public string HandlerMethodName { get; set; }
        public Type DeclaringType { get; set; }
        public string DeclaringTypeName { get; set; }
        public object TargetObject { get; set; }
        public Type TargetType { get; set; }
        public string TargetTypeName { get; set; }
        public bool IsPrivate { get; set; }
        public bool IsStatic { get; set; }
        public int ParameterCount { get; set; }
        public Delegate Delegate { get; set; }

        public override string ToString()
        {
            var access = IsPrivate ? "private" : "public";
            var staticMod = IsStatic ? "static " : "";
            return $"{EventName} -> {access} {staticMod}{TargetTypeName}.{HandlerMethodName}()";
        }

        /// <summary>
        /// Gets a detailed description of the handler.
        /// </summary>
        public string GetDetailedDescription()
        {
            return $"Event: {EventName}\n" +
                   $"Handler: {HandlerMethodName}\n" +
                   $"Declaring Type: {DeclaringTypeName}\n" +
                   $"Target Type: {TargetTypeName}\n" +
                   $"Access: {(IsPrivate ? "private" : "public")}\n" +
                   $"Static: {IsStatic}\n" +
                   $"Parameters: {ParameterCount}";
        }
    }

    /// <summary>
    /// Information about a control and its attached event handlers.
    /// </summary>
    public class ControlEventInfo
    {
        public Control Control { get; set; }
        public string ControlName { get; set; }
        public Type ControlType { get; set; }
        public string ControlTypeName { get; set; }
        public int Depth { get; set; }
        public List<EventHandlerDetail> EventHandlers { get; set; }
        public int EventCount { get; set; }

        public override string ToString()
        {
            return $"{ControlName} ({ControlTypeName}) - {EventCount} handlers";
        }
    }

    /// <summary>
    /// Internal class for tracking event info and keys.
    /// </summary>
    internal class EventKeyEntry
    {
        public EventInfo EventInfo { get; set; }
        public object Key { get; set; }
        public Type DeclaringType { get; set; }
    }

    /// <summary>
    /// Statistics about event handlers in a control hierarchy.
    /// </summary>
    public class EventStatistics
    {
        public int TotalControlsWithHandlers { get; set; }
        public int TotalHandlers { get; set; }
        public Dictionary<string, int> HandlersByEvent { get; set; } = new Dictionary<string, int>();
        public Dictionary<string, int> HandlersByTargetType { get; set; } = new Dictionary<string, int>();
        public Dictionary<string, int> HandlersByControlType { get; set; } = new Dictionary<string, int>();

        public string GetSummary()
        {
            var summary = $"Controls with handlers: {TotalControlsWithHandlers}\n" +
                         $"Total handlers: {TotalHandlers}\n\n";

            if (HandlersByEvent.Count > 0)
            {
                summary += "By Event:\n";
                foreach (var kvp in HandlersByEvent.OrderByDescending(x => x.Value).Take(10))
                {
                    summary += $"  {kvp.Key}: {kvp.Value}\n";
                }
            }

            return summary;
        }
    }

    #endregion
}
