using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
using MusicBeePlugin.Clouseau.Core;
using NLog;

namespace MusicBeePlugin.Clouseau.Introspection
{
    /// <summary>
    /// Intercepts MusicBee API delegate calls to log usage, track performance,
    /// and detect anomalies. Wraps API delegates at initialization to provide
    /// call statistics and slow operation detection.
    /// </summary>
    public class ApiInterceptor
    {
        private static readonly Logger Logger = NLog.LogManager.GetCurrentClassLogger();

        // Performance thresholds (from settings)
        private readonly int _warnThresholdMs;
        private readonly int _errorThresholdMs;
        private readonly bool _enabled;

        // Statistics tracking
        private readonly ConcurrentDictionary<string, ApiCallStatistics> _callStatistics;
        private readonly ConcurrentQueue<ApiCallRecord> _recentCalls;
        private const int MaxRecentCalls = 1000;

        // Sequence number for ordering
        private long _callSequence = 0;

        // Events for slow call detection
        public event EventHandler<SlowApiCallEventArgs> SlowCallDetected;
        public event EventHandler<ApiAnomalyEventArgs> AnomalyDetected;

        // Reference to original API interface
        private object _originalApi;
        private bool _isIntercepting;

        /// <summary>
        /// Creates a new ApiInterceptor with the specified settings.
        /// </summary>
        /// <param name="enabled">Whether API interception is enabled.</param>
        /// <param name="warnThresholdMs">Warning threshold in milliseconds (default 100).</param>
        /// <param name="errorThresholdMs">Error threshold in milliseconds (default 500).</param>
        public ApiInterceptor(bool enabled = true, int warnThresholdMs = 100, int errorThresholdMs = 500)
        {
            _enabled = enabled;
            _warnThresholdMs = warnThresholdMs;
            _errorThresholdMs = errorThresholdMs;
            _callStatistics = new ConcurrentDictionary<string, ApiCallStatistics>();
            _recentCalls = new ConcurrentQueue<ApiCallRecord>();
        }

        /// <summary>
        /// Gets whether API interception is currently active.
        /// </summary>
        public bool IsIntercepting => _isIntercepting;

        /// <summary>
        /// Gets the warning threshold in milliseconds.
        /// </summary>
        public int WarnThresholdMs => _warnThresholdMs;

        /// <summary>
        /// Gets the error threshold in milliseconds.
        /// </summary>
        public int ErrorThresholdMs => _errorThresholdMs;

        /// <summary>
        /// Gets the total number of API calls intercepted.
        /// </summary>
        public long TotalCallCount => _callStatistics.Values.Sum(s => s.CallCount);

        /// <summary>
        /// Wraps MusicBee API delegates to enable interception.
        /// Call this after MusicBeeApiInterface.Initialise() to wrap all delegates.
        /// </summary>
        /// <param name="mbApiInterface">The MusicBeeApiInterface struct instance.</param>
        /// <returns>True if wrapping succeeded, false otherwise.</returns>
        public bool WrapApiDelegates(ref Plugin.MusicBeeApiInterface mbApiInterface)
        {
            if (!_enabled)
            {
                Logger.Info("API interception is disabled in settings");
                return false;
            }

            try
            {
                Logger.Info("Wrapping MusicBee API delegates for interception...");

                // Store reference to original API
                _originalApi = mbApiInterface;

                // Get all delegate fields from the struct
                var apiType = typeof(Plugin.MusicBeeApiInterface);
                var fields = apiType.GetFields(BindingFlags.Public | BindingFlags.Instance);

                int wrappedCount = 0;
                int skippedCount = 0;

                foreach (var field in fields)
                {
                    // Skip non-delegate fields (like InterfaceVersion, ApiRevision)
                    if (!typeof(Delegate).IsAssignableFrom(field.FieldType))
                    {
                        continue;
                    }

                    try
                    {
                        var originalDelegate = field.GetValue(mbApiInterface) as Delegate;
                        if (originalDelegate == null)
                        {
                            skippedCount++;
                            continue;
                        }

                        // Create wrapper delegate
                        var wrapper = CreateWrapperDelegate(field.Name, originalDelegate);
                        if (wrapper != null)
                        {
                            // Set the wrapped delegate back
                            // Note: structs are value types, so we need to box/unbox carefully
                            var boxed = (object)mbApiInterface;
                            field.SetValue(boxed, wrapper);
                            mbApiInterface = (Plugin.MusicBeeApiInterface)boxed;
                            wrappedCount++;
                        }
                        else
                        {
                            skippedCount++;
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Trace($"Could not wrap delegate {field.Name}: {ex.Message}");
                        skippedCount++;
                    }
                }

                _isIntercepting = true;
                Logger.Info($"API interception enabled: {wrappedCount} delegates wrapped, {skippedCount} skipped");
                return true;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Failed to wrap API delegates");
                return false;
            }
        }

        /// <summary>
        /// Creates a wrapper delegate that logs timing and call information.
        /// </summary>
        private Delegate CreateWrapperDelegate(string methodName, Delegate originalDelegate)
        {
            var delegateType = originalDelegate.GetType();
            var invokeMethod = delegateType.GetMethod("Invoke");
            if (invokeMethod == null) return null;

            var parameters = invokeMethod.GetParameters();
            var returnType = invokeMethod.ReturnType;

            // Create the appropriate wrapper based on delegate signature
            // Due to complexity of creating generic wrappers at runtime,
            // we'll use a dictionary of known delegate types

            return CreateTypedWrapper(methodName, originalDelegate, delegateType);
        }

        /// <summary>
        /// Creates a typed wrapper for a specific delegate type.
        /// </summary>
        private Delegate CreateTypedWrapper(string methodName, Delegate original, Type delegateType)
        {
            // Handle common MusicBee API delegate patterns
            // These are the most frequently used delegates that benefit from interception

            try
            {
                // String return, no params (e.g., NowPlaying_GetFileUrl)
                if (delegateType == typeof(Plugin.NowPlaying_GetFileUrlDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetFileUrlDelegate)original;
                    return new Plugin.NowPlaying_GetFileUrlDelegate(() => WrapCall(methodName, () => orig()));
                }

                // String return, MetaDataType param (e.g., NowPlaying_GetFileTag)
                if (delegateType == typeof(Plugin.NowPlaying_GetFileTagDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetFileTagDelegate)original;
                    return new Plugin.NowPlaying_GetFileTagDelegate((field) => WrapCall(methodName, () => orig(field)));
                }

                // Int return, no params (e.g., NowPlaying_GetDuration, Player_GetPosition)
                if (delegateType == typeof(Plugin.NowPlaying_GetDurationDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetDurationDelegate)original;
                    return new Plugin.NowPlaying_GetDurationDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetPositionDelegate))
                {
                    var orig = (Plugin.Player_GetPositionDelegate)original;
                    return new Plugin.Player_GetPositionDelegate(() => WrapCall(methodName, () => orig()));
                }

                // PlayState return (Player_GetPlayState)
                if (delegateType == typeof(Plugin.Player_GetPlayStateDelegate))
                {
                    var orig = (Plugin.Player_GetPlayStateDelegate)original;
                    return new Plugin.Player_GetPlayStateDelegate(() => WrapCall(methodName, () => orig()));
                }

                // Float return (Player_GetVolume)
                if (delegateType == typeof(Plugin.Player_GetVolumeDelegate))
                {
                    var orig = (Plugin.Player_GetVolumeDelegate)original;
                    return new Plugin.Player_GetVolumeDelegate(() => WrapCall(methodName, () => orig()));
                }

                // Bool return, no params (Player_GetShuffle, Player_GetMute, etc.)
                if (delegateType == typeof(Plugin.Player_GetShuffleDelegate))
                {
                    var orig = (Plugin.Player_GetShuffleDelegate)original;
                    return new Plugin.Player_GetShuffleDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetMuteDelegate))
                {
                    var orig = (Plugin.Player_GetMuteDelegate)original;
                    return new Plugin.Player_GetMuteDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetEqualiserEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetEqualiserEnabledDelegate)original;
                    return new Plugin.Player_GetEqualiserEnabledDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetScrobbleEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetScrobbleEnabledDelegate)original;
                    return new Plugin.Player_GetScrobbleEnabledDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetAutoDjEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetAutoDjEnabledDelegate)original;
                    return new Plugin.Player_GetAutoDjEnabledDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetStopAfterCurrentEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetStopAfterCurrentEnabledDelegate)original;
                    return new Plugin.Player_GetStopAfterCurrentEnabledDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetCrossfadeDelegate))
                {
                    var orig = (Plugin.Player_GetCrossfadeDelegate)original;
                    return new Plugin.Player_GetCrossfadeDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetDspEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetDspEnabledDelegate)original;
                    return new Plugin.Player_GetDspEnabledDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetShowTimeRemainingDelegate))
                {
                    var orig = (Plugin.Player_GetShowTimeRemainingDelegate)original;
                    return new Plugin.Player_GetShowTimeRemainingDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetShowRatingTrackDelegate))
                {
                    var orig = (Plugin.Player_GetShowRatingTrackDelegate)original;
                    return new Plugin.Player_GetShowRatingTrackDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_GetShowRatingLoveDelegate))
                {
                    var orig = (Plugin.Player_GetShowRatingLoveDelegate)original;
                    return new Plugin.Player_GetShowRatingLoveDelegate(() => WrapCall(methodName, () => orig()));
                }

                // RepeatMode return (Player_GetRepeat)
                if (delegateType == typeof(Plugin.Player_GetRepeatDelegate))
                {
                    var orig = (Plugin.Player_GetRepeatDelegate)original;
                    return new Plugin.Player_GetRepeatDelegate(() => WrapCall(methodName, () => orig()));
                }

                // ReplayGainMode return (Player_GetReplayGainMode)
                if (delegateType == typeof(Plugin.Player_GetReplayGainModeDelegate))
                {
                    var orig = (Plugin.Player_GetReplayGainModeDelegate)original;
                    return new Plugin.Player_GetReplayGainModeDelegate(() => WrapCall(methodName, () => orig()));
                }

                // Bool return, no params - Player actions
                if (delegateType == typeof(Plugin.Player_ActionDelegate))
                {
                    var orig = (Plugin.Player_ActionDelegate)original;
                    return new Plugin.Player_ActionDelegate(() => WrapCall(methodName, () => orig()));
                }

                // Bool set methods
                if (delegateType == typeof(Plugin.Player_SetVolumeDelegate))
                {
                    var orig = (Plugin.Player_SetVolumeDelegate)original;
                    return new Plugin.Player_SetVolumeDelegate((v) => WrapCall(methodName, () => orig(v)));
                }

                if (delegateType == typeof(Plugin.Player_SetPositionDelegate))
                {
                    var orig = (Plugin.Player_SetPositionDelegate)original;
                    return new Plugin.Player_SetPositionDelegate((p) => WrapCall(methodName, () => orig(p)));
                }

                if (delegateType == typeof(Plugin.Player_SetMuteDelegate))
                {
                    var orig = (Plugin.Player_SetMuteDelegate)original;
                    return new Plugin.Player_SetMuteDelegate((m) => WrapCall(methodName, () => orig(m)));
                }

                if (delegateType == typeof(Plugin.Player_SetShuffleDelegate))
                {
                    var orig = (Plugin.Player_SetShuffleDelegate)original;
                    return new Plugin.Player_SetShuffleDelegate((s) => WrapCall(methodName, () => orig(s)));
                }

                if (delegateType == typeof(Plugin.Player_SetRepeatDelegate))
                {
                    var orig = (Plugin.Player_SetRepeatDelegate)original;
                    return new Plugin.Player_SetRepeatDelegate((r) => WrapCall(methodName, () => orig(r)));
                }

                if (delegateType == typeof(Plugin.Player_SetEqualiserEnabledDelegate))
                {
                    var orig = (Plugin.Player_SetEqualiserEnabledDelegate)original;
                    return new Plugin.Player_SetEqualiserEnabledDelegate((e) => WrapCall(methodName, () => orig(e)));
                }

                if (delegateType == typeof(Plugin.Player_SetScrobbleEnabledDelegate))
                {
                    var orig = (Plugin.Player_SetScrobbleEnabledDelegate)original;
                    return new Plugin.Player_SetScrobbleEnabledDelegate((e) => WrapCall(methodName, () => orig(e)));
                }

                if (delegateType == typeof(Plugin.Player_SetDspEnabledDelegate))
                {
                    var orig = (Plugin.Player_SetDspEnabledDelegate)original;
                    return new Plugin.Player_SetDspEnabledDelegate((e) => WrapCall(methodName, () => orig(e)));
                }

                if (delegateType == typeof(Plugin.Player_SetCrossfadeDelegate))
                {
                    var orig = (Plugin.Player_SetCrossfadeDelegate)original;
                    return new Plugin.Player_SetCrossfadeDelegate((c) => WrapCall(methodName, () => orig(c)));
                }

                if (delegateType == typeof(Plugin.Player_SetReplayGainModeDelegate))
                {
                    var orig = (Plugin.Player_SetReplayGainModeDelegate)original;
                    return new Plugin.Player_SetReplayGainModeDelegate((m) => WrapCall(methodName, () => orig(m)));
                }

                // NowPlayingList methods
                if (delegateType == typeof(Plugin.NowPlayingList_GetCurrentIndexDelegate))
                {
                    var orig = (Plugin.NowPlayingList_GetCurrentIndexDelegate)original;
                    return new Plugin.NowPlayingList_GetCurrentIndexDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_GetNextIndexDelegate))
                {
                    var orig = (Plugin.NowPlayingList_GetNextIndexDelegate)original;
                    return new Plugin.NowPlayingList_GetNextIndexDelegate((o) => WrapCall(methodName, () => orig(o)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_IsAnyPriorTracksDelegate))
                {
                    var orig = (Plugin.NowPlayingList_IsAnyPriorTracksDelegate)original;
                    return new Plugin.NowPlayingList_IsAnyPriorTracksDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_IsAnyFollowingTracksDelegate))
                {
                    var orig = (Plugin.NowPlayingList_IsAnyFollowingTracksDelegate)original;
                    return new Plugin.NowPlayingList_IsAnyFollowingTracksDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_ActionDelegate))
                {
                    var orig = (Plugin.NowPlayingList_ActionDelegate)original;
                    return new Plugin.NowPlayingList_ActionDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_GetFileUrlDelegate))
                {
                    var orig = (Plugin.NowPlayingList_GetFileUrlDelegate)original;
                    return new Plugin.NowPlayingList_GetFileUrlDelegate((i) => WrapCall(methodName, () => orig(i)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_GetFilePropertyDelegate))
                {
                    var orig = (Plugin.NowPlayingList_GetFilePropertyDelegate)original;
                    return new Plugin.NowPlayingList_GetFilePropertyDelegate((i, t) => WrapCall(methodName, () => orig(i, t)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_GetFileTagDelegate))
                {
                    var orig = (Plugin.NowPlayingList_GetFileTagDelegate)original;
                    return new Plugin.NowPlayingList_GetFileTagDelegate((i, f) => WrapCall(methodName, () => orig(i, f)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_FileActionDelegate))
                {
                    var orig = (Plugin.NowPlayingList_FileActionDelegate)original;
                    return new Plugin.NowPlayingList_FileActionDelegate((s) => WrapCall(methodName, () => orig(s)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_FilesActionDelegate))
                {
                    var orig = (Plugin.NowPlayingList_FilesActionDelegate)original;
                    return new Plugin.NowPlayingList_FilesActionDelegate((s) => WrapCall(methodName, () => orig(s)));
                }

                if (delegateType == typeof(Plugin.NowPlayingList_RemoveAtDelegate))
                {
                    var orig = (Plugin.NowPlayingList_RemoveAtDelegate)original;
                    return new Plugin.NowPlayingList_RemoveAtDelegate((i) => WrapCall(methodName, () => orig(i)));
                }

                // Library methods
                if (delegateType == typeof(Plugin.Library_GetFilePropertyDelegate))
                {
                    var orig = (Plugin.Library_GetFilePropertyDelegate)original;
                    return new Plugin.Library_GetFilePropertyDelegate((u, t) => WrapCall(methodName, () => orig(u, t)));
                }

                if (delegateType == typeof(Plugin.Library_GetFileTagDelegate))
                {
                    var orig = (Plugin.Library_GetFileTagDelegate)original;
                    return new Plugin.Library_GetFileTagDelegate((u, f) => WrapCall(methodName, () => orig(u, f)));
                }

                if (delegateType == typeof(Plugin.Library_SetFileTagDelegate))
                {
                    var orig = (Plugin.Library_SetFileTagDelegate)original;
                    return new Plugin.Library_SetFileTagDelegate((u, f, v) => WrapCall(methodName, () => orig(u, f, v)));
                }

                if (delegateType == typeof(Plugin.Library_CommitTagsToFileDelegate))
                {
                    var orig = (Plugin.Library_CommitTagsToFileDelegate)original;
                    return new Plugin.Library_CommitTagsToFileDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                if (delegateType == typeof(Plugin.Library_GetLyricsDelegate))
                {
                    var orig = (Plugin.Library_GetLyricsDelegate)original;
                    return new Plugin.Library_GetLyricsDelegate((u, t) => WrapCall(methodName, () => orig(u, t)));
                }

                if (delegateType == typeof(Plugin.Library_QueryFilesDelegate))
                {
                    var orig = (Plugin.Library_QueryFilesDelegate)original;
                    return new Plugin.Library_QueryFilesDelegate((q) => WrapCall(methodName, () => orig(q)));
                }

                if (delegateType == typeof(Plugin.Library_QueryGetNextFileDelegate))
                {
                    var orig = (Plugin.Library_QueryGetNextFileDelegate)original;
                    return new Plugin.Library_QueryGetNextFileDelegate(() => WrapCall(methodName, () => orig()));
                }

                // NowPlaying additional methods
                if (delegateType == typeof(Plugin.NowPlaying_GetFilePropertyDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetFilePropertyDelegate)original;
                    return new Plugin.NowPlaying_GetFilePropertyDelegate((t) => WrapCall(methodName, () => orig(t)));
                }

                if (delegateType == typeof(Plugin.NowPlaying_GetLyricsDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetLyricsDelegate)original;
                    return new Plugin.NowPlaying_GetLyricsDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlaying_GetArtworkDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetArtworkDelegate)original;
                    return new Plugin.NowPlaying_GetArtworkDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.NowPlaying_GetArtistPictureDelegate))
                {
                    var orig = (Plugin.NowPlaying_GetArtistPictureDelegate)original;
                    return new Plugin.NowPlaying_GetArtistPictureDelegate((f) => WrapCall(methodName, () => orig(f)));
                }

                // Playlist methods
                if (delegateType == typeof(Plugin.Playlist_QueryPlaylistsDelegate))
                {
                    var orig = (Plugin.Playlist_QueryPlaylistsDelegate)original;
                    return new Plugin.Playlist_QueryPlaylistsDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Playlist_QueryGetNextPlaylistDelegate))
                {
                    var orig = (Plugin.Playlist_QueryGetNextPlaylistDelegate)original;
                    return new Plugin.Playlist_QueryGetNextPlaylistDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Playlist_GetTypeDelegate))
                {
                    var orig = (Plugin.Playlist_GetTypeDelegate)original;
                    return new Plugin.Playlist_GetTypeDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                if (delegateType == typeof(Plugin.Playlist_QueryFilesDelegate))
                {
                    var orig = (Plugin.Playlist_QueryFilesDelegate)original;
                    return new Plugin.Playlist_QueryFilesDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                if (delegateType == typeof(Plugin.Playlist_GetNameDelegate))
                {
                    var orig = (Plugin.Playlist_GetNameDelegate)original;
                    return new Plugin.Playlist_GetNameDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                if (delegateType == typeof(Plugin.Playlist_IsInListDelegate))
                {
                    var orig = (Plugin.Playlist_IsInListDelegate)original;
                    return new Plugin.Playlist_IsInListDelegate((p, f) => WrapCall(methodName, () => orig(p, f)));
                }

                if (delegateType == typeof(Plugin.Playlist_DeletePlaylistDelegate))
                {
                    var orig = (Plugin.Playlist_DeletePlaylistDelegate)original;
                    return new Plugin.Playlist_DeletePlaylistDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                if (delegateType == typeof(Plugin.Playlist_PlayNowDelegate))
                {
                    var orig = (Plugin.Playlist_PlayNowDelegate)original;
                    return new Plugin.Playlist_PlayNowDelegate((u) => WrapCall(methodName, () => orig(u)));
                }

                // Settings methods
                if (delegateType == typeof(Plugin.Setting_GetPersistentStoragePathDelegate))
                {
                    var orig = (Plugin.Setting_GetPersistentStoragePathDelegate)original;
                    return new Plugin.Setting_GetPersistentStoragePathDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Setting_GetSkinDelegate))
                {
                    var orig = (Plugin.Setting_GetSkinDelegate)original;
                    return new Plugin.Setting_GetSkinDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Setting_GetFieldNameDelegate))
                {
                    var orig = (Plugin.Setting_GetFieldNameDelegate)original;
                    return new Plugin.Setting_GetFieldNameDelegate((f) => WrapCall(methodName, () => orig(f)));
                }

                if (delegateType == typeof(Plugin.Setting_GetDataTypeDelegate))
                {
                    var orig = (Plugin.Setting_GetDataTypeDelegate)original;
                    return new Plugin.Setting_GetDataTypeDelegate((f) => WrapCall(methodName, () => orig(f)));
                }

                if (delegateType == typeof(Plugin.Setting_IsWindowBordersSkinnedDelegate))
                {
                    var orig = (Plugin.Setting_IsWindowBordersSkinnedDelegate)original;
                    return new Plugin.Setting_IsWindowBordersSkinnedDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Setting_GetDefaultFontDelegate))
                {
                    var orig = (Plugin.Setting_GetDefaultFontDelegate)original;
                    return new Plugin.Setting_GetDefaultFontDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Setting_GetLastFmUserIdDelegate))
                {
                    var orig = (Plugin.Setting_GetLastFmUserIdDelegate)original;
                    return new Plugin.Setting_GetLastFmUserIdDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Setting_GetWebProxyDelegate))
                {
                    var orig = (Plugin.Setting_GetWebProxyDelegate)original;
                    return new Plugin.Setting_GetWebProxyDelegate(() => WrapCall(methodName, () => orig()));
                }

                // MB_* methods
                if (delegateType == typeof(Plugin.MB_WindowHandleDelegate))
                {
                    var orig = (Plugin.MB_WindowHandleDelegate)original;
                    return new Plugin.MB_WindowHandleDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.MB_RefreshPanelsDelegate))
                {
                    var orig = (Plugin.MB_RefreshPanelsDelegate)original;
                    return new Plugin.MB_RefreshPanelsDelegate(() => WrapCallVoid(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.MB_TraceDelegate))
                {
                    var orig = (Plugin.MB_TraceDelegate)original;
                    return new Plugin.MB_TraceDelegate((s) => WrapCallVoid(methodName, () => orig(s)));
                }

                if (delegateType == typeof(Plugin.MB_ReleaseStringDelegate))
                {
                    var orig = (Plugin.MB_ReleaseStringDelegate)original;
                    return new Plugin.MB_ReleaseStringDelegate((s) => WrapCallVoid(methodName, () => orig(s)));
                }

                if (delegateType == typeof(Plugin.MB_SendNotificationDelegate))
                {
                    var orig = (Plugin.MB_SendNotificationDelegate)original;
                    return new Plugin.MB_SendNotificationDelegate((t) => WrapCallVoid(methodName, () => orig(t)));
                }

                if (delegateType == typeof(Plugin.MB_GetLocalisationDelegate))
                {
                    var orig = (Plugin.MB_GetLocalisationDelegate)original;
                    return new Plugin.MB_GetLocalisationDelegate((i, d) => WrapCall(methodName, () => orig(i, d)));
                }

                if (delegateType == typeof(Plugin.MB_ShowNowPlayingAssistantDelegate))
                {
                    var orig = (Plugin.MB_ShowNowPlayingAssistantDelegate)original;
                    return new Plugin.MB_ShowNowPlayingAssistantDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.MB_InvokeCommandDelegate))
                {
                    var orig = (Plugin.MB_InvokeCommandDelegate)original;
                    return new Plugin.MB_InvokeCommandDelegate((c, p) => WrapCall(methodName, () => orig(c, p)));
                }

                if (delegateType == typeof(Plugin.MB_SetWindowSizeDelegate))
                {
                    var orig = (Plugin.MB_SetWindowSizeDelegate)original;
                    return new Plugin.MB_SetWindowSizeDelegate((w, h) => WrapCall(methodName, () => orig(w, h)));
                }

                if (delegateType == typeof(Plugin.Player_ShowEqualiserDelegate))
                {
                    var orig = (Plugin.Player_ShowEqualiserDelegate)original;
                    return new Plugin.Player_ShowEqualiserDelegate(() => WrapCall(methodName, () => orig()));
                }

                if (delegateType == typeof(Plugin.Player_QueueRandomTracksDelegate))
                {
                    var orig = (Plugin.Player_QueueRandomTracksDelegate)original;
                    return new Plugin.Player_QueueRandomTracksDelegate((c) => WrapCall(methodName, () => orig(c)));
                }

                if (delegateType == typeof(Plugin.Player_GetButtonEnabledDelegate))
                {
                    var orig = (Plugin.Player_GetButtonEnabledDelegate)original;
                    return new Plugin.Player_GetButtonEnabledDelegate((b) => WrapCall(methodName, () => orig(b)));
                }

                // For delegates we don't have explicit wrappers for, log at trace level
                // and record in statistics without actually wrapping
                RecordUnsupportedDelegate(methodName);
                return null;
            }
            catch (Exception ex)
            {
                Logger.Trace($"Could not create wrapper for {methodName}: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// Wraps a function call with timing and statistics collection.
        /// </summary>
        private T WrapCall<T>(string methodName, Func<T> action)
        {
            var sw = Stopwatch.StartNew();
            Exception exception = null;
            T result = default(T);

            try
            {
                result = action();
                return result;
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                sw.Stop();
                RecordCall(methodName, sw.ElapsedMilliseconds, exception);
            }
        }

        /// <summary>
        /// Wraps a void function call with timing and statistics collection.
        /// </summary>
        private void WrapCallVoid(string methodName, Action action)
        {
            var sw = Stopwatch.StartNew();
            Exception exception = null;

            try
            {
                action();
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                sw.Stop();
                RecordCall(methodName, sw.ElapsedMilliseconds, exception);
            }
        }

        /// <summary>
        /// Records a call in statistics and checks for slow operation thresholds.
        /// </summary>
        private void RecordCall(string methodName, long elapsedMs, Exception exception = null)
        {
            try
            {
                var sequence = Interlocked.Increment(ref _callSequence);

                // Update statistics
                var stats = _callStatistics.GetOrAdd(methodName, _ => new ApiCallStatistics(methodName));
                stats.RecordCall(elapsedMs, exception != null);

                // Record in recent calls buffer
                var record = new ApiCallRecord
                {
                    SequenceNumber = sequence,
                    Timestamp = DateTime.Now,
                    MethodName = methodName,
                    DurationMs = elapsedMs,
                    HadError = exception != null,
                    CallingThread = Thread.CurrentThread.ManagedThreadId
                };

                _recentCalls.Enqueue(record);

                // Trim buffer if needed
                while (_recentCalls.Count > MaxRecentCalls)
                {
                    _recentCalls.TryDequeue(out _);
                }

                // Log API call
                Core.LogManager.LogApiCall(methodName, elapsedMs, exception?.Message);

                // Check for slow call thresholds
                if (elapsedMs >= _errorThresholdMs)
                {
                    Logger.Error($"SLOW API CALL: {methodName} took {elapsedMs}ms (threshold: {_errorThresholdMs}ms)");
                    Core.LogManager.Performance.Error($"Slow API call: {methodName} ({elapsedMs}ms)");

                    OnSlowCallDetected(new SlowApiCallEventArgs
                    {
                        MethodName = methodName,
                        DurationMs = elapsedMs,
                        Severity = SlowCallSeverity.Error,
                        Timestamp = DateTime.Now
                    });
                }
                else if (elapsedMs >= _warnThresholdMs)
                {
                    Logger.Warn($"Slow API call: {methodName} took {elapsedMs}ms (threshold: {_warnThresholdMs}ms)");
                    Core.LogManager.Performance.Warn($"Slow API call: {methodName} ({elapsedMs}ms)");

                    OnSlowCallDetected(new SlowApiCallEventArgs
                    {
                        MethodName = methodName,
                        DurationMs = elapsedMs,
                        Severity = SlowCallSeverity.Warning,
                        Timestamp = DateTime.Now
                    });
                }

                // Check for anomalies (rapid successive calls, unusual patterns)
                CheckForAnomalies(methodName, stats);
            }
            catch (Exception ex)
            {
                // Don't let logging errors break the actual API call
                Logger.Trace($"Error recording API call: {ex.Message}");
            }
        }

        /// <summary>
        /// Records that a delegate type is not explicitly supported for wrapping.
        /// </summary>
        private void RecordUnsupportedDelegate(string methodName)
        {
            var stats = _callStatistics.GetOrAdd(methodName, _ => new ApiCallStatistics(methodName));
            stats.IsWrapped = false;
        }

        /// <summary>
        /// Checks for anomalous call patterns.
        /// </summary>
        private void CheckForAnomalies(string methodName, ApiCallStatistics stats)
        {
            // Detect rapid-fire calls (more than 100 calls per second to same method)
            var recentSameMethods = _recentCalls
                .Where(r => r.MethodName == methodName && r.Timestamp > DateTime.Now.AddSeconds(-1))
                .Count();

            if (recentSameMethods > 100)
            {
                OnAnomalyDetected(new ApiAnomalyEventArgs
                {
                    MethodName = methodName,
                    AnomalyType = ApiAnomalyType.RapidFireCalls,
                    Description = $"Method {methodName} called {recentSameMethods} times in last second",
                    Timestamp = DateTime.Now
                });
            }

            // Detect high error rate (more than 10% errors in last 100 calls)
            if (stats.CallCount >= 100)
            {
                var errorRate = (double)stats.ErrorCount / stats.CallCount;
                if (errorRate > 0.1)
                {
                    OnAnomalyDetected(new ApiAnomalyEventArgs
                    {
                        MethodName = methodName,
                        AnomalyType = ApiAnomalyType.HighErrorRate,
                        Description = $"Method {methodName} has {errorRate:P1} error rate",
                        Timestamp = DateTime.Now
                    });
                }
            }
        }

        /// <summary>
        /// Gets statistics for all tracked API calls.
        /// </summary>
        public IReadOnlyDictionary<string, ApiCallStatistics> GetCallStatistics()
        {
            return new Dictionary<string, ApiCallStatistics>(_callStatistics);
        }

        /// <summary>
        /// Gets statistics for a specific API method.
        /// </summary>
        public ApiCallStatistics GetMethodStatistics(string methodName)
        {
            return _callStatistics.TryGetValue(methodName, out var stats) ? stats : null;
        }

        /// <summary>
        /// Gets recent API call records.
        /// </summary>
        public IEnumerable<ApiCallRecord> GetRecentCalls(int count = 100)
        {
            // Convert to array and take last N elements (.TakeLast not available in .NET Framework)
            var allRecords = _recentCalls.ToArray();
            var startIndex = Math.Max(0, allRecords.Length - count);
            return allRecords.Skip(startIndex).ToList();
        }

        /// <summary>
        /// Gets the top N slowest API calls by average duration.
        /// </summary>
        public IEnumerable<ApiCallStatistics> GetSlowestMethods(int count = 10)
        {
            return _callStatistics.Values
                .Where(s => s.CallCount > 0 && s.IsWrapped)
                .OrderByDescending(s => s.AverageDurationMs)
                .Take(count);
        }

        /// <summary>
        /// Gets the most frequently called API methods.
        /// </summary>
        public IEnumerable<ApiCallStatistics> GetMostCalledMethods(int count = 10)
        {
            return _callStatistics.Values
                .Where(s => s.CallCount > 0)
                .OrderByDescending(s => s.CallCount)
                .Take(count);
        }

        /// <summary>
        /// Resets all collected statistics.
        /// </summary>
        public void ResetStatistics()
        {
            foreach (var stats in _callStatistics.Values)
            {
                stats.Reset();
            }
            while (_recentCalls.TryDequeue(out _)) { }
            Logger.Info("API call statistics reset");
        }

        /// <summary>
        /// Gets a summary of API usage statistics.
        /// </summary>
        public string GetStatisticsSummary()
        {
            var sb = new System.Text.StringBuilder();
            sb.AppendLine("=== API Interception Statistics ===");
            sb.AppendLine($"Interception Active: {_isIntercepting}");
            sb.AppendLine($"Total Calls: {TotalCallCount:N0}");
            sb.AppendLine($"Tracked Methods: {_callStatistics.Count}");
            sb.AppendLine($"Wrapped Methods: {_callStatistics.Values.Count(s => s.IsWrapped)}");
            sb.AppendLine();

            var slowest = GetSlowestMethods(5).ToList();
            if (slowest.Any())
            {
                sb.AppendLine("--- Slowest Methods (by avg) ---");
                foreach (var stats in slowest)
                {
                    sb.AppendLine($"  {stats.MethodName}: {stats.AverageDurationMs:F1}ms avg ({stats.CallCount} calls)");
                }
                sb.AppendLine();
            }

            var mostCalled = GetMostCalledMethods(5).ToList();
            if (mostCalled.Any())
            {
                sb.AppendLine("--- Most Called Methods ---");
                foreach (var stats in mostCalled)
                {
                    sb.AppendLine($"  {stats.MethodName}: {stats.CallCount:N0} calls");
                }
            }

            return sb.ToString();
        }

        protected virtual void OnSlowCallDetected(SlowApiCallEventArgs e)
        {
            SlowCallDetected?.Invoke(this, e);
        }

        protected virtual void OnAnomalyDetected(ApiAnomalyEventArgs e)
        {
            AnomalyDetected?.Invoke(this, e);
            Logger.Warn($"API Anomaly Detected: {e.AnomalyType} - {e.Description}");
        }
    }

    /// <summary>
    /// Statistics for a single API method.
    /// </summary>
    public class ApiCallStatistics
    {
        private long _callCount;
        private long _errorCount;
        private long _totalDurationMs;
        private long _maxDurationMs;
        private long _minDurationMs = long.MaxValue;
        private readonly object _lock = new object();

        public ApiCallStatistics(string methodName)
        {
            MethodName = methodName;
            FirstCallTime = DateTime.Now;
            IsWrapped = true;
        }

        public string MethodName { get; }
        public DateTime FirstCallTime { get; private set; }
        public DateTime LastCallTime { get; private set; }
        public bool IsWrapped { get; set; }

        public long CallCount => Interlocked.Read(ref _callCount);
        public long ErrorCount => Interlocked.Read(ref _errorCount);
        public long TotalDurationMs => Interlocked.Read(ref _totalDurationMs);
        public long MaxDurationMs => Interlocked.Read(ref _maxDurationMs);
        public long MinDurationMs => _minDurationMs == long.MaxValue ? 0 : Interlocked.Read(ref _minDurationMs);

        public double AverageDurationMs => CallCount > 0 ? (double)TotalDurationMs / CallCount : 0;
        public double CallsPerSecond
        {
            get
            {
                var elapsed = (DateTime.Now - FirstCallTime).TotalSeconds;
                return elapsed > 0 ? CallCount / elapsed : 0;
            }
        }

        public void RecordCall(long durationMs, bool hadError)
        {
            Interlocked.Increment(ref _callCount);
            Interlocked.Add(ref _totalDurationMs, durationMs);

            if (hadError)
            {
                Interlocked.Increment(ref _errorCount);
            }

            // Update min/max (using lock for simplicity)
            lock (_lock)
            {
                if (durationMs > _maxDurationMs) _maxDurationMs = durationMs;
                if (durationMs < _minDurationMs) _minDurationMs = durationMs;
                LastCallTime = DateTime.Now;
            }
        }

        public void Reset()
        {
            lock (_lock)
            {
                _callCount = 0;
                _errorCount = 0;
                _totalDurationMs = 0;
                _maxDurationMs = 0;
                _minDurationMs = long.MaxValue;
                FirstCallTime = DateTime.Now;
            }
        }

        public override string ToString()
        {
            return $"{MethodName}: {CallCount} calls, avg {AverageDurationMs:F1}ms, max {MaxDurationMs}ms";
        }
    }

    /// <summary>
    /// Record of a single API call.
    /// </summary>
    public class ApiCallRecord
    {
        public long SequenceNumber { get; set; }
        public DateTime Timestamp { get; set; }
        public string MethodName { get; set; }
        public long DurationMs { get; set; }
        public bool HadError { get; set; }
        public int CallingThread { get; set; }
    }

    /// <summary>
    /// Event args for slow API call detection.
    /// </summary>
    public class SlowApiCallEventArgs : EventArgs
    {
        public string MethodName { get; set; }
        public long DurationMs { get; set; }
        public SlowCallSeverity Severity { get; set; }
        public DateTime Timestamp { get; set; }
    }

    /// <summary>
    /// Severity level for slow API calls.
    /// </summary>
    public enum SlowCallSeverity
    {
        Warning,
        Error
    }

    /// <summary>
    /// Event args for API anomaly detection.
    /// </summary>
    public class ApiAnomalyEventArgs : EventArgs
    {
        public string MethodName { get; set; }
        public ApiAnomalyType AnomalyType { get; set; }
        public string Description { get; set; }
        public DateTime Timestamp { get; set; }
    }

    /// <summary>
    /// Types of API call anomalies that can be detected.
    /// </summary>
    public enum ApiAnomalyType
    {
        /// <summary>
        /// Same method called more than 100 times per second.
        /// </summary>
        RapidFireCalls,

        /// <summary>
        /// Method has higher than 10% error rate.
        /// </summary>
        HighErrorRate,

        /// <summary>
        /// Sudden spike in call duration.
        /// </summary>
        DurationSpike,

        /// <summary>
        /// Unusual call pattern detected.
        /// </summary>
        UnusualPattern
    }
}
