using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using MusicBeePlugin.Clouseau.Core;

namespace MusicBeePlugin.Clouseau.Introspection
{
    /// <summary>
    /// Tracks MusicBee command registrations (MB_RegisterCommand) and
    /// invocations (MB_InvokeCommand), as well as menu item registrations.
    /// Provides rich context for command/automation tracking.
    /// </summary>
    public class CommandRegistry : IDisposable
    {
        private readonly ConcurrentDictionary<string, CommandRegistration> _commands;
        private readonly ConcurrentDictionary<string, MenuRegistration> _menus;
        private readonly ConcurrentQueue<CommandInvocation> _invocationHistory;
        private const int MaxInvocationHistory = 500;
        private long _invocationSequence = 0;
        private bool _disposed;

        /// <summary>
        /// Event raised when a new command is registered.
        /// </summary>
        public event EventHandler<CommandRegistration> CommandRegistered;

        /// <summary>
        /// Event raised when a command is invoked.
        /// </summary>
        public event EventHandler<CommandInvocation> CommandInvoked;

        /// <summary>
        /// Event raised when a menu item is registered.
        /// </summary>
        public event EventHandler<MenuRegistration> MenuRegistered;

        public CommandRegistry()
        {
            _commands = new ConcurrentDictionary<string, CommandRegistration>(StringComparer.OrdinalIgnoreCase);
            _menus = new ConcurrentDictionary<string, MenuRegistration>(StringComparer.OrdinalIgnoreCase);
            _invocationHistory = new ConcurrentQueue<CommandInvocation>();
        }

        /// <summary>
        /// Gets the total number of registered commands.
        /// </summary>
        public int CommandCount => _commands.Count;

        /// <summary>
        /// Gets the total number of registered menu items.
        /// </summary>
        public int MenuCount => _menus.Count;

        /// <summary>
        /// Gets the total command invocation count.
        /// </summary>
        public long InvocationCount => _invocationSequence;

        /// <summary>
        /// Records a command registration via MB_RegisterCommand.
        /// </summary>
        /// <param name="commandName">The command name being registered.</param>
        /// <param name="pluginSource">The source plugin (if known).</param>
        /// <param name="hotkey">Associated hotkey description.</param>
        public void RecordCommandRegistration(string commandName, string pluginSource = null, string hotkey = null)
        {
            if (string.IsNullOrEmpty(commandName)) return;

            var registration = new CommandRegistration
            {
                CommandName = commandName,
                PluginSource = pluginSource ?? DetectCallingPlugin(),
                Hotkey = hotkey,
                RegisteredAt = DateTime.Now
            };

            _commands.AddOrUpdate(commandName, registration, (key, existing) =>
            {
                // Update registration count
                existing.ReregistrationCount++;
                existing.LastRegisteredAt = DateTime.Now;
                return existing;
            });

            LogManager.Core.Debug($"Command registered: {commandName} by {registration.PluginSource}");
            OnCommandRegistered(registration);
        }

        /// <summary>
        /// Records a command invocation via MB_InvokeCommand.
        /// </summary>
        /// <param name="commandType">The command type from the Command enum.</param>
        /// <param name="parameter">The command parameter.</param>
        /// <param name="success">Whether the invocation succeeded.</param>
        /// <param name="durationMs">Duration of the call in milliseconds.</param>
        public void RecordCommandInvocation(Plugin.Command commandType, object parameter, bool success, long durationMs)
        {
            var invocation = new CommandInvocation
            {
                SequenceNumber = System.Threading.Interlocked.Increment(ref _invocationSequence),
                CommandType = commandType.ToString(),
                CommandValue = (int)commandType,
                Parameter = parameter?.ToString() ?? "",
                ParameterType = parameter?.GetType().Name ?? "null",
                Success = success,
                DurationMs = durationMs,
                InvokedAt = DateTime.Now,
                PluginSource = DetectCallingPlugin()
            };

            _invocationHistory.Enqueue(invocation);
            while (_invocationHistory.Count > MaxInvocationHistory)
            {
                _invocationHistory.TryDequeue(out _);
            }

            LogManager.Core.Debug($"Command invoked: {commandType} with {parameter} - {(success ? "OK" : "FAIL")} ({durationMs}ms)");
            OnCommandInvoked(invocation);
        }

        /// <summary>
        /// Records a menu item registration via MB_AddMenuItem.
        /// </summary>
        /// <param name="menuPath">The menu path (e.g., "context.Main/Custom/MyItem").</param>
        /// <param name="hotkeyDescription">The hotkey description.</param>
        /// <param name="menuItem">The ToolStripItem created.</param>
        /// <param name="pluginSource">The source plugin (if known).</param>
        public void RecordMenuRegistration(string menuPath, string hotkeyDescription, ToolStripItem menuItem = null, string pluginSource = null)
        {
            if (string.IsNullOrEmpty(menuPath)) return;

            var registration = new MenuRegistration
            {
                MenuPath = menuPath,
                HotkeyDescription = hotkeyDescription,
                PluginSource = pluginSource ?? DetectCallingPlugin(),
                RegisteredAt = DateTime.Now,
                MenuItemText = menuItem?.Text,
                MenuItemName = menuItem?.Name
            };

            _menus.AddOrUpdate(menuPath, registration, (key, existing) =>
            {
                existing.ReregistrationCount++;
                existing.LastRegisteredAt = DateTime.Now;
                return existing;
            });

            LogManager.Core.Debug($"Menu registered: {menuPath} by {registration.PluginSource}");
            OnMenuRegistered(registration);
        }

        /// <summary>
        /// Gets all registered commands.
        /// </summary>
        public IReadOnlyList<CommandRegistration> GetCommands()
        {
            return _commands.Values.OrderBy(c => c.CommandName).ToList();
        }

        /// <summary>
        /// Gets all registered menu items.
        /// </summary>
        public IReadOnlyList<MenuRegistration> GetMenus()
        {
            return _menus.Values.OrderBy(m => m.MenuPath).ToList();
        }

        /// <summary>
        /// Gets recent command invocations.
        /// </summary>
        /// <param name="count">Maximum number of invocations to return.</param>
        public IReadOnlyList<CommandInvocation> GetRecentInvocations(int count = 100)
        {
            var all = _invocationHistory.ToArray();
            return all.Skip(Math.Max(0, all.Length - count)).ToList();
        }

        /// <summary>
        /// Gets a summary of command/menu registrations.
        /// </summary>
        public string GetSummary()
        {
            var sb = new System.Text.StringBuilder();
            sb.AppendLine("=== Command Registry Summary ===");
            sb.AppendLine($"Commands: {_commands.Count}");
            sb.AppendLine($"Menus: {_menus.Count}");
            sb.AppendLine($"Invocations: {_invocationSequence}");
            sb.AppendLine();

            if (_commands.Any())
            {
                sb.AppendLine("--- Registered Commands ---");
                foreach (var cmd in GetCommands())
                {
                    sb.AppendLine($"  {cmd.CommandName} (from {cmd.PluginSource})");
                }
            }

            if (_menus.Any())
            {
                sb.AppendLine("--- Registered Menus ---");
                foreach (var menu in GetMenus().Take(20))
                {
                    sb.AppendLine($"  {menu.MenuPath}");
                }
                if (_menus.Count > 20)
                    sb.AppendLine($"  ... and {_menus.Count - 20} more");
            }

            return sb.ToString();
        }

        /// <summary>
        /// Attempts to determine which plugin made the call by examining the call stack.
        /// </summary>
        private string DetectCallingPlugin()
        {
            try
            {
                var stackTrace = new System.Diagnostics.StackTrace();
                foreach (var frame in stackTrace.GetFrames() ?? Array.Empty<System.Diagnostics.StackFrame>())
                {
                    var method = frame.GetMethod();
                    var assembly = method?.DeclaringType?.Assembly;
                    if (assembly != null)
                    {
                        var name = assembly.GetName().Name;
                        if (name != null && name.StartsWith("mb_") && name != "mb_clouseau")
                        {
                            return name;
                        }
                    }
                }
            }
            catch { }
            return "Unknown";
        }

        protected virtual void OnCommandRegistered(CommandRegistration reg)
            => CommandRegistered?.Invoke(this, reg);

        protected virtual void OnCommandInvoked(CommandInvocation inv)
            => CommandInvoked?.Invoke(this, inv);

        protected virtual void OnMenuRegistered(MenuRegistration reg)
            => MenuRegistered?.Invoke(this, reg);

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _commands.Clear();
                    _menus.Clear();
                }
                _disposed = true;
            }
        }
    }

    /// <summary>
    /// Represents a registered MusicBee command via MB_RegisterCommand.
    /// </summary>
    public class CommandRegistration
    {
        public string CommandName { get; set; }
        public string PluginSource { get; set; }
        public string Hotkey { get; set; }
        public DateTime RegisteredAt { get; set; }
        public DateTime? LastRegisteredAt { get; set; }
        public int ReregistrationCount { get; set; }
    }

    /// <summary>
    /// Represents a registered menu item via MB_AddMenuItem.
    /// </summary>
    public class MenuRegistration
    {
        public string MenuPath { get; set; }
        public string HotkeyDescription { get; set; }
        public string PluginSource { get; set; }
        public DateTime RegisteredAt { get; set; }
        public DateTime? LastRegisteredAt { get; set; }
        public int ReregistrationCount { get; set; }
        public string MenuItemText { get; set; }
        public string MenuItemName { get; set; }
    }

    /// <summary>
    /// Represents a command invocation event via MB_InvokeCommand.
    /// </summary>
    public class CommandInvocation
    {
        public long SequenceNumber { get; set; }
        public string CommandType { get; set; }
        public int CommandValue { get; set; }
        public string Parameter { get; set; }
        public string ParameterType { get; set; }
        public bool Success { get; set; }
        public long DurationMs { get; set; }
        public DateTime InvokedAt { get; set; }
        public string PluginSource { get; set; }
    }
}
