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 walking and inspecting MusicBee's menu system.
    /// Can discover MainMenu, ContextMenus, and ToolStrip menus.
    /// </summary>
    public class MenuWalker
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

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

        /// <summary>
        /// Gets all menu items from a form's main menu and any attached context menus.
        /// </summary>
        /// <param name="form">The form to inspect.</param>
        /// <returns>List of all menu items.</returns>
        public List<MenuItemDetail> GetMenuItems(Form form)
        {
            var allItems = new List<MenuItemDetail>();

            if (form == null)
                return allItems;

            try
            {
                // Find MainMenuStrip
                if (form.MainMenuStrip != null)
                {
                    allItems.AddRange(GetMenuStripItems(form.MainMenuStrip, "MainMenu"));
                }

                // Find all MenuStrips in the form
                foreach (Control control in form.Controls)
                {
                    if (control is MenuStrip menuStrip && menuStrip != form.MainMenuStrip)
                    {
                        allItems.AddRange(GetMenuStripItems(menuStrip, $"MenuStrip:{menuStrip.Name}"));
                    }
                }

                // Find ContextMenuStrips attached to controls
                var contextMenus = GetContextMenus(form);
                foreach (var ctx in contextMenus)
                {
                    allItems.AddRange(GetContextMenuItems(ctx.ContextMenu, ctx.AttachedTo));
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error getting menu items from form");
            }

            return allItems;
        }

        /// <summary>
        /// Gets all items from a MenuStrip.
        /// </summary>
        /// <param name="menuStrip">The menu strip to walk.</param>
        /// <param name="source">Source identifier for the menu.</param>
        /// <returns>List of menu item details.</returns>
        public List<MenuItemDetail> GetMenuStripItems(MenuStrip menuStrip, string source = null)
        {
            var items = new List<MenuItemDetail>();

            if (menuStrip == null)
                return items;

            foreach (ToolStripItem item in menuStrip.Items)
            {
                CollectMenuItems(item, "", items, source ?? menuStrip.Name, 0);
            }

            return items;
        }

        /// <summary>
        /// Gets all items from a ContextMenuStrip.
        /// </summary>
        /// <param name="contextMenu">The context menu to walk.</param>
        /// <param name="attachedToControl">The control name this context menu is attached to.</param>
        /// <returns>List of menu item details.</returns>
        public List<MenuItemDetail> GetContextMenuItems(ContextMenuStrip contextMenu, string attachedToControl = null)
        {
            var items = new List<MenuItemDetail>();

            if (contextMenu == null)
                return items;

            var source = $"ContextMenu:{contextMenu.Name}";
            if (!string.IsNullOrEmpty(attachedToControl))
            {
                source += $"@{attachedToControl}";
            }

            foreach (ToolStripItem item in contextMenu.Items)
            {
                CollectMenuItems(item, "", items, source, 0);
            }

            return items;
        }

        private void CollectMenuItems(ToolStripItem item, string parentPath, List<MenuItemDetail> items, string source, int depth)
        {
            if (item == null)
                return;

            var currentPath = string.IsNullOrEmpty(parentPath)
                ? item.Text?.Replace("&", "") ?? item.Name
                : $"{parentPath} > {item.Text?.Replace("&", "") ?? item.Name}";

            var detail = new MenuItemDetail
            {
                Path = currentPath,
                Text = item.Text,
                Name = item.Name,
                ItemType = item.GetType().Name,
                Source = source,
                Depth = depth,
                Enabled = item.Enabled,
                Visible = item.Visible,
                Available = item.Available,
                ToolStripItem = item
            };

            // Get additional properties for menu items
            if (item is ToolStripMenuItem menuItem)
            {
                detail.ShortcutKeys = menuItem.ShortcutKeys;
                detail.ShortcutKeyDisplayString = menuItem.ShortcutKeyDisplayString;
                detail.Checked = menuItem.Checked;
                detail.CheckOnClick = menuItem.CheckOnClick;
                detail.HasDropDownItems = menuItem.HasDropDownItems;
                detail.DropDownItemCount = menuItem.DropDownItems.Count;

                // Try to get click handler
                var clickHandler = GetClickHandler(menuItem);
                if (clickHandler != null)
                {
                    detail.ClickHandler = clickHandler.Method;
                    detail.ClickHandlerMethodName = clickHandler.Method?.Name;
                    detail.ClickHandlerTargetType = clickHandler.Target?.GetType();
                    detail.ClickHandlerTargetTypeName = clickHandler.Target?.GetType()?.FullName ?? "[Static]";
                    detail.HasClickHandler = true;
                }
            }
            else if (item is ToolStripSeparator)
            {
                detail.IsSeparator = true;
            }

            items.Add(detail);

            // Recurse into dropdown items
            if (item is ToolStripDropDownItem dropDown)
            {
                foreach (ToolStripItem child in dropDown.DropDownItems)
                {
                    CollectMenuItems(child, currentPath, items, source, depth + 1);
                }
            }
        }

        /// <summary>
        /// Gets the click handler delegate for a ToolStripItem.
        /// </summary>
        private Delegate GetClickHandler(ToolStripItem item)
        {
            try
            {
                // Get the EventClick key
                var clickField = typeof(ToolStripItem).GetField("EventClick", AllMembers);
                if (clickField == null)
                    return null;

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

                var eventList = eventsField.GetValue(item) as EventHandlerList;
                if (eventList == null)
                    return null;

                var key = clickField.GetValue(null);
                if (key == null)
                    return null;

                var handler = eventList[key];
                if (handler != null)
                {
                    return handler.GetInvocationList().FirstOrDefault();
                }
            }
            catch (Exception ex)
            {
                Logger.Trace($"Error getting click handler for {item.Name}: {ex.Message}");
            }

            return null;
        }

        /// <summary>
        /// Finds all ContextMenuStrips attached to controls in a control hierarchy.
        /// </summary>
        /// <param name="root">The root control to start from.</param>
        /// <returns>List of context menu information.</returns>
        public List<ContextMenuInfo> GetContextMenus(Control root)
        {
            var contextMenus = new List<ContextMenuInfo>();

            if (root == null)
                return contextMenus;

            CollectContextMenus(root, contextMenus);

            return contextMenus;
        }

        private void CollectContextMenus(Control control, List<ContextMenuInfo> contextMenus)
        {
            if (control.ContextMenuStrip != null)
            {
                // Avoid duplicates
                var existing = contextMenus.FirstOrDefault(c => c.ContextMenu == control.ContextMenuStrip);
                if (existing == null)
                {
                    contextMenus.Add(new ContextMenuInfo
                    {
                        ContextMenu = control.ContextMenuStrip,
                        ContextMenuName = control.ContextMenuStrip.Name,
                        AttachedTo = string.IsNullOrEmpty(control.Name) ? $"[{control.GetType().Name}]" : control.Name,
                        AttachedToControl = control,
                        ItemCount = CountMenuItems(control.ContextMenuStrip)
                    });
                }
                else
                {
                    // Menu is shared - add this control to the attached list
                    existing.AttachedTo += $", {(string.IsNullOrEmpty(control.Name) ? $"[{control.GetType().Name}]" : control.Name)}";
                }
            }

            foreach (Control child in control.Controls)
            {
                CollectContextMenus(child, contextMenus);
            }
        }

        private int CountMenuItems(ContextMenuStrip menu)
        {
            int count = 0;
            foreach (ToolStripItem item in menu.Items)
            {
                count += CountMenuItemsRecursive(item);
            }
            return count;
        }

        private int CountMenuItemsRecursive(ToolStripItem item)
        {
            int count = 1;
            if (item is ToolStripDropDownItem dropDown)
            {
                foreach (ToolStripItem child in dropDown.DropDownItems)
                {
                    count += CountMenuItemsRecursive(child);
                }
            }
            return count;
        }

        /// <summary>
        /// Invokes the click handler for a menu item.
        /// </summary>
        /// <param name="item">The menu item to click.</param>
        /// <returns>True if successfully invoked.</returns>
        public bool InvokeMenuItem(ToolStripMenuItem item)
        {
            if (item == null)
                return false;

            try
            {
                // Method 1: Try to use PerformClick
                item.PerformClick();
                Logger.Info($"Invoked menu item via PerformClick: {item.Text}");
                return true;
            }
            catch (Exception ex1)
            {
                Logger.Debug($"PerformClick failed for {item.Text}: {ex1.Message}");

                try
                {
                    // Method 2: Invoke the click handler directly
                    var handler = GetClickHandler(item);
                    if (handler != null)
                    {
                        handler.DynamicInvoke(item, EventArgs.Empty);
                        Logger.Info($"Invoked menu item via handler: {item.Text}");
                        return true;
                    }
                }
                catch (Exception ex2)
                {
                    Logger.Error(ex2, $"Error invoking menu item handler for {item.Text}");
                }
            }

            return false;
        }

        /// <summary>
        /// Invokes a menu item by its path.
        /// </summary>
        /// <param name="form">The form containing the menu.</param>
        /// <param name="path">The menu path (e.g., "File > Exit").</param>
        /// <returns>True if successfully invoked.</returns>
        public bool InvokeMenuItemByPath(Form form, string path)
        {
            if (form == null || string.IsNullOrEmpty(path))
                return false;

            var items = GetMenuItems(form);
            var targetItem = items.FirstOrDefault(i =>
                string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) &&
                i.ToolStripItem is ToolStripMenuItem);

            if (targetItem?.ToolStripItem is ToolStripMenuItem menuItem)
            {
                return InvokeMenuItem(menuItem);
            }

            Logger.Warn($"Menu item not found: {path}");
            return false;
        }

        /// <summary>
        /// Searches for menu items matching a pattern.
        /// </summary>
        /// <param name="form">The form to search.</param>
        /// <param name="searchPattern">The search pattern (case-insensitive).</param>
        /// <returns>Matching menu items.</returns>
        public List<MenuItemDetail> SearchMenuItems(Form form, string searchPattern)
        {
            if (form == null || string.IsNullOrEmpty(searchPattern))
                return new List<MenuItemDetail>();

            var allItems = GetMenuItems(form);
            var pattern = searchPattern.ToLowerInvariant();

            return allItems.Where(item =>
                (item.Text?.ToLowerInvariant().Contains(pattern) ?? false) ||
                (item.Name?.ToLowerInvariant().Contains(pattern) ?? false) ||
                (item.Path?.ToLowerInvariant().Contains(pattern) ?? false) ||
                (item.ClickHandlerMethodName?.ToLowerInvariant().Contains(pattern) ?? false)
            ).ToList();
        }

        /// <summary>
        /// Gets a hierarchical tree of menu items.
        /// </summary>
        /// <param name="form">The form to analyze.</param>
        /// <returns>Root menu nodes.</returns>
        public List<MenuTreeNode> GetMenuTree(Form form)
        {
            var roots = new List<MenuTreeNode>();

            if (form?.MainMenuStrip != null)
            {
                foreach (ToolStripItem item in form.MainMenuStrip.Items)
                {
                    if (item is ToolStripMenuItem menuItem)
                    {
                        roots.Add(BuildMenuTreeNode(menuItem, 0));
                    }
                }
            }

            return roots;
        }

        private MenuTreeNode BuildMenuTreeNode(ToolStripMenuItem item, int depth)
        {
            var node = new MenuTreeNode
            {
                MenuItem = item,
                Text = item.Text?.Replace("&", "") ?? item.Name,
                Name = item.Name,
                Depth = depth,
                Enabled = item.Enabled,
                Visible = item.Visible,
                Checked = item.Checked,
                ShortcutKeys = item.ShortcutKeys,
                HasClickHandler = GetClickHandler(item) != null,
                Children = new List<MenuTreeNode>()
            };

            foreach (ToolStripItem child in item.DropDownItems)
            {
                if (child is ToolStripMenuItem childMenuItem)
                {
                    node.Children.Add(BuildMenuTreeNode(childMenuItem, depth + 1));
                }
                else if (child is ToolStripSeparator)
                {
                    node.Children.Add(new MenuTreeNode
                    {
                        IsSeparator = true,
                        Text = "---",
                        Depth = depth + 1
                    });
                }
            }

            return node;
        }

        /// <summary>
        /// Gets menu statistics for a form.
        /// </summary>
        public MenuStatistics GetMenuStatistics(Form form)
        {
            var stats = new MenuStatistics();
            var items = GetMenuItems(form);

            stats.TotalMenuItems = items.Count;
            stats.MenuItemsWithHandlers = items.Count(i => i.HasClickHandler);
            stats.MenuItemsWithShortcuts = items.Count(i => i.ShortcutKeys != Keys.None);
            stats.SeparatorCount = items.Count(i => i.IsSeparator);
            stats.DisabledItemCount = items.Count(i => !i.Enabled);
            stats.HiddenItemCount = items.Count(i => !i.Visible);
            stats.CheckedItemCount = items.Count(i => i.Checked);

            var contextMenus = GetContextMenus(form);
            stats.ContextMenuCount = contextMenus.Count;
            stats.TotalContextMenuItems = contextMenus.Sum(c => c.ItemCount);

            // Group by handler target type
            stats.HandlersByTargetType = items
                .Where(i => i.HasClickHandler)
                .GroupBy(i => i.ClickHandlerTargetTypeName ?? "Unknown")
                .ToDictionary(g => g.Key, g => g.Count());

            return stats;
        }
    }

    #region Data Models

    /// <summary>
    /// Detailed information about a menu item.
    /// </summary>
    public class MenuItemDetail
    {
        public string Path { get; set; }
        public string Text { get; set; }
        public string Name { get; set; }
        public string ItemType { get; set; }
        public string Source { get; set; }
        public int Depth { get; set; }
        public bool Enabled { get; set; }
        public bool Visible { get; set; }
        public bool Available { get; set; }
        public Keys ShortcutKeys { get; set; }
        public string ShortcutKeyDisplayString { get; set; }
        public bool Checked { get; set; }
        public bool CheckOnClick { get; set; }
        public bool HasDropDownItems { get; set; }
        public int DropDownItemCount { get; set; }
        public bool IsSeparator { get; set; }
        public bool HasClickHandler { get; set; }
        public MethodInfo ClickHandler { get; set; }
        public string ClickHandlerMethodName { get; set; }
        public Type ClickHandlerTargetType { get; set; }
        public string ClickHandlerTargetTypeName { get; set; }
        public ToolStripItem ToolStripItem { get; set; }

        public override string ToString()
        {
            var handler = HasClickHandler ? $" -> {ClickHandlerMethodName}()" : "";
            var shortcut = ShortcutKeys != Keys.None ? $" [{ShortcutKeys}]" : "";
            return $"{Path}{shortcut}{handler}";
        }

        /// <summary>
        /// Gets a detailed description of the menu item.
        /// </summary>
        public string GetDetailedDescription()
        {
            var desc = $"Path: {Path}\n" +
                      $"Text: {Text}\n" +
                      $"Name: {Name}\n" +
                      $"Type: {ItemType}\n" +
                      $"Source: {Source}\n" +
                      $"Enabled: {Enabled}\n" +
                      $"Visible: {Visible}\n";

            if (ShortcutKeys != Keys.None)
                desc += $"Shortcut: {ShortcutKeys}\n";

            if (Checked)
                desc += "Checked: Yes\n";

            if (HasClickHandler)
                desc += $"Handler: {ClickHandlerTargetTypeName}.{ClickHandlerMethodName}()\n";

            return desc;
        }
    }

    /// <summary>
    /// Information about a context menu.
    /// </summary>
    public class ContextMenuInfo
    {
        public ContextMenuStrip ContextMenu { get; set; }
        public string ContextMenuName { get; set; }
        public string AttachedTo { get; set; }
        public Control AttachedToControl { get; set; }
        public int ItemCount { get; set; }

        public override string ToString()
        {
            return $"{ContextMenuName} @ {AttachedTo} ({ItemCount} items)";
        }
    }

    /// <summary>
    /// Tree node representation of a menu item.
    /// </summary>
    public class MenuTreeNode
    {
        public ToolStripMenuItem MenuItem { get; set; }
        public string Text { get; set; }
        public string Name { get; set; }
        public int Depth { get; set; }
        public bool Enabled { get; set; }
        public bool Visible { get; set; }
        public bool Checked { get; set; }
        public Keys ShortcutKeys { get; set; }
        public bool HasClickHandler { get; set; }
        public bool IsSeparator { get; set; }
        public List<MenuTreeNode> Children { get; set; }

        public override string ToString()
        {
            var indent = new string(' ', Depth * 2);
            return $"{indent}{Text}";
        }
    }

    /// <summary>
    /// Statistics about menu items in a form.
    /// </summary>
    public class MenuStatistics
    {
        public int TotalMenuItems { get; set; }
        public int MenuItemsWithHandlers { get; set; }
        public int MenuItemsWithShortcuts { get; set; }
        public int SeparatorCount { get; set; }
        public int DisabledItemCount { get; set; }
        public int HiddenItemCount { get; set; }
        public int CheckedItemCount { get; set; }
        public int ContextMenuCount { get; set; }
        public int TotalContextMenuItems { get; set; }
        public Dictionary<string, int> HandlersByTargetType { get; set; } = new Dictionary<string, int>();

        public string GetSummary()
        {
            var summary = $"Total Menu Items: {TotalMenuItems}\n" +
                         $"With Handlers: {MenuItemsWithHandlers}\n" +
                         $"With Shortcuts: {MenuItemsWithShortcuts}\n" +
                         $"Separators: {SeparatorCount}\n" +
                         $"Disabled: {DisabledItemCount}\n" +
                         $"Hidden: {HiddenItemCount}\n" +
                         $"Checked: {CheckedItemCount}\n" +
                         $"Context Menus: {ContextMenuCount}\n" +
                         $"Context Menu Items: {TotalContextMenuItems}\n";

            if (HandlersByTargetType.Count > 0)
            {
                summary += "\nHandlers by Target:\n";
                foreach (var kvp in HandlersByTargetType.OrderByDescending(x => x.Value))
                {
                    summary += $"  {kvp.Key}: {kvp.Value}\n";
                }
            }

            return summary;
        }
    }

    #endregion
}
