using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using MusicBeePlugin.Clouseau.Introspection;

namespace MusicBeePlugin.Clouseau.Logging
{
    /// <summary>
    /// Captures and logs all MusicBee NotificationType events with rich context.
    /// </summary>
    public class EventLogger : IDisposable
    {
        private static readonly Logger NLogger = LogManager.GetLogger("Clouseau.Events");

        private readonly Plugin.MusicBeeApiInterface _mbApi;
        private readonly LogBuffer _buffer;
        private bool _disposed;

        /// <summary>
        /// Reference to sync tracker for rich sync context.
        /// Set from Plugin.cs during initialization.
        /// </summary>
        public SyncTracker SyncTracker { get; set; }

        /// <summary>
        /// Reference to command registry for tracking.
        /// Set from Plugin.cs during initialization.
        /// </summary>
        public CommandRegistry CommandRegistry { get; set; }

        /// <summary>
        /// Event fired when a new log entry is added.
        /// </summary>
        public event EventHandler<LogEntry> LogEntryAdded;

        /// <summary>
        /// Gets the log buffer containing all captured entries.
        /// </summary>
        public LogBuffer Buffer => _buffer;

        /// <summary>
        /// Creates a new EventLogger instance.
        /// </summary>
        /// <param name="mbApi">The MusicBee API interface.</param>
        /// <param name="bufferSize">Maximum number of entries to keep in the buffer.</param>
        public EventLogger(Plugin.MusicBeeApiInterface mbApi, int bufferSize = 10000)
        {
            _mbApi = mbApi;
            _buffer = new LogBuffer(bufferSize);
        }

        /// <summary>
        /// Handles a MusicBee notification and logs it with appropriate context.
        /// </summary>
        /// <param name="type">The notification type from MusicBee.</param>
        public void HandleNotification(Plugin.NotificationType type)
        {
            try
            {
                var entry = CreateLogEntry(type);
                if (entry != null)
                {
                    _buffer.Add(entry);
                    WriteToNLog(entry);
                    OnLogEntryAdded(entry);
                }
            }
            catch (Exception ex)
            {
                var errorEntry = new LogEntry(
                    EventCategory.System,
                    "LoggingError",
                    -1,
                    $"Error handling notification {type}: {ex.Message}",
                    LogLevel.Error
                );
                errorEntry.WithContext("Exception", ex.ToString());
                _buffer.Add(errorEntry);
                WriteToNLog(errorEntry);
                OnLogEntryAdded(errorEntry);
            }
        }

        /// <summary>
        /// Creates a log entry for the given notification type with rich context.
        /// </summary>
        private LogEntry CreateLogEntry(Plugin.NotificationType type)
        {
            var category = GetCategory(type);
            var message = GetMessage(type);
            var entry = new LogEntry(category, type.ToString(), (int)type, message);

            // Get current source file for context
            string sourceFile = null;
            try
            {
                sourceFile = _mbApi.NowPlaying_GetFileUrl?.Invoke();
            }
            catch { /* Ignore errors getting source file */ }

            if (!string.IsNullOrEmpty(sourceFile))
            {
                entry.WithSourceFile(sourceFile);
            }

            // Add rich context based on event type
            AddContext(entry, type);

            return entry;
        }

        /// <summary>
        /// Gets the category for a notification type.
        /// </summary>
        private string GetCategory(Plugin.NotificationType type)
        {
            switch (type)
            {
                // Core events
                case Plugin.NotificationType.PluginStartup:
                case Plugin.NotificationType.ShutdownStarted:
                case Plugin.NotificationType.MusicBeeStarted:
                    return EventCategory.Core;

                // Player events
                case Plugin.NotificationType.TrackChanged:
                case Plugin.NotificationType.TrackChanging:
                case Plugin.NotificationType.PlayStateChanged:
                case Plugin.NotificationType.VolumeMuteChanged:
                case Plugin.NotificationType.VolumeLevelChanged:
                case Plugin.NotificationType.AutoDjStarted:
                case Plugin.NotificationType.AutoDjStopped:
                case Plugin.NotificationType.PlayerRepeatChanged:
                case Plugin.NotificationType.PlayerShuffleChanged:
                case Plugin.NotificationType.PlayerEqualiserOnOffChanged:
                case Plugin.NotificationType.PlayerScrobbleChanged:
                case Plugin.NotificationType.ReplayGainChanged:
                case Plugin.NotificationType.StopAfterCurrentChanged:
                case Plugin.NotificationType.NowPlayingArtworkReady:
                case Plugin.NotificationType.NowPlayingLyricsReady:
                    return EventCategory.Player;

                // Queue events
                case Plugin.NotificationType.PlayingTracksChanged:
                case Plugin.NotificationType.PlayingTracksQueueChanged:
                case Plugin.NotificationType.NowPlayingListEnded:
#pragma warning disable CS0618 // Type or member is obsolete
                case Plugin.NotificationType.NowPlayingListChanged:
#pragma warning restore CS0618
                    return EventCategory.Queue;

                // Library events
                case Plugin.NotificationType.FileAddedToLibrary:
                case Plugin.NotificationType.FileAddedToInbox:
                case Plugin.NotificationType.FileDeleting:
                case Plugin.NotificationType.FileDeleted:
                case Plugin.NotificationType.LibrarySwitched:
                    return EventCategory.Library;

                // Tags events
                case Plugin.NotificationType.TagsChanging:
                case Plugin.NotificationType.TagsChanged:
                case Plugin.NotificationType.RatingChanging:
                case Plugin.NotificationType.RatingChanged:
                case Plugin.NotificationType.PlayCountersChanged:
                    return EventCategory.Tags;

                // Playlist events
                case Plugin.NotificationType.PlaylistCreated:
                case Plugin.NotificationType.PlaylistUpdated:
                case Plugin.NotificationType.PlaylistDeleted:
                    return EventCategory.Playlist;

                // Download events
                case Plugin.NotificationType.DownloadCompleted:
                    return EventCategory.Download;

                // Sync events
                case Plugin.NotificationType.SynchCompleted:
                    return EventCategory.Sync;

                // UI events
                case Plugin.NotificationType.EmbedInPanel:
                case Plugin.NotificationType.ApplicationWindowChanged:
                    return EventCategory.UI;

                // System events
                case Plugin.NotificationType.ScreenSaverActivating:
                    return EventCategory.System;

                default:
                    return EventCategory.System;
            }
        }

        /// <summary>
        /// Gets a human-readable message for the notification type.
        /// </summary>
        private string GetMessage(Plugin.NotificationType type)
        {
            switch (type)
            {
                case Plugin.NotificationType.PluginStartup:
                    return "Plugin initialized successfully";
                case Plugin.NotificationType.TrackChanging:
                    return "Track is about to change";
                case Plugin.NotificationType.TrackChanged:
                    return GetTrackChangedMessage();
                case Plugin.NotificationType.PlayStateChanged:
                    return GetPlayStateMessage();
                case Plugin.NotificationType.AutoDjStarted:
                    return "AutoDJ mode started";
                case Plugin.NotificationType.AutoDjStopped:
                    return "AutoDJ mode stopped";
                case Plugin.NotificationType.VolumeMuteChanged:
                    return GetMuteMessage();
                case Plugin.NotificationType.VolumeLevelChanged:
                    return GetVolumeMessage();
#pragma warning disable CS0618
                case Plugin.NotificationType.NowPlayingListChanged:
#pragma warning restore CS0618
                    return "Now playing list changed (obsolete event)";
                case Plugin.NotificationType.NowPlayingArtworkReady:
                    return "Artwork loaded for current track";
                case Plugin.NotificationType.NowPlayingLyricsReady:
                    return "Lyrics loaded for current track";
                case Plugin.NotificationType.TagsChanging:
                    return "Tags are about to be modified";
                case Plugin.NotificationType.TagsChanged:
                    return "Tags were modified";
                case Plugin.NotificationType.RatingChanging:
                    return "Rating is about to change";
                case Plugin.NotificationType.RatingChanged:
                    return GetRatingMessage();
                case Plugin.NotificationType.PlayCountersChanged:
                    return "Play/skip counters updated";
                case Plugin.NotificationType.ScreenSaverActivating:
                    return "Screen saver is about to activate";
                case Plugin.NotificationType.ShutdownStarted:
                    return "MusicBee is shutting down";
                case Plugin.NotificationType.NowPlayingListEnded:
                    return "Reached end of now playing list";
                case Plugin.NotificationType.EmbedInPanel:
                    return "Plugin panel embedded";
                case Plugin.NotificationType.PlayerRepeatChanged:
                    return GetRepeatMessage();
                case Plugin.NotificationType.PlayerShuffleChanged:
                    return GetShuffleMessage();
                case Plugin.NotificationType.PlayerEqualiserOnOffChanged:
                    return GetEqMessage();
                case Plugin.NotificationType.PlayerScrobbleChanged:
                    return GetScrobbleMessage();
                case Plugin.NotificationType.ReplayGainChanged:
                    return GetReplayGainMessage();
                case Plugin.NotificationType.FileDeleting:
                    return "File is about to be deleted";
                case Plugin.NotificationType.FileDeleted:
                    return "File was deleted";
                case Plugin.NotificationType.ApplicationWindowChanged:
                    return "Application window state changed";
                case Plugin.NotificationType.StopAfterCurrentChanged:
                    return GetStopAfterCurrentMessage();
                case Plugin.NotificationType.LibrarySwitched:
                    return "Active library was switched";
                case Plugin.NotificationType.FileAddedToLibrary:
                    return "New file added to library";
                case Plugin.NotificationType.FileAddedToInbox:
                    return "New file added to inbox";
                case Plugin.NotificationType.SynchCompleted:
                    return "Device sync completed";
                case Plugin.NotificationType.DownloadCompleted:
                    return "Download completed";
                case Plugin.NotificationType.MusicBeeStarted:
                    return "MusicBee fully initialized";
                case Plugin.NotificationType.PlayingTracksChanged:
                    return "Now playing list was modified";
                case Plugin.NotificationType.PlayingTracksQueueChanged:
                    return "Queue portion of now playing list changed";
                case Plugin.NotificationType.PlaylistCreated:
                    return "New playlist created";
                case Plugin.NotificationType.PlaylistUpdated:
                    return "Playlist was modified";
                case Plugin.NotificationType.PlaylistDeleted:
                    return "Playlist was deleted";
                default:
                    return $"Unknown notification: {type}";
            }
        }

        /// <summary>
        /// Adds rich context data to the log entry based on event type.
        /// </summary>
        private void AddContext(LogEntry entry, Plugin.NotificationType type)
        {
            try
            {
                switch (type)
                {
                    // Core events
                    case Plugin.NotificationType.PluginStartup:
                        AddPluginStartupContext(entry);
                        break;
                    case Plugin.NotificationType.MusicBeeStarted:
                        AddMusicBeeStartedContext(entry);
                        break;
                    case Plugin.NotificationType.ShutdownStarted:
                        AddShutdownContext(entry);
                        break;

                    // Player events
                    case Plugin.NotificationType.TrackChanging:
                        AddTrackChangingContext(entry);
                        break;
                    case Plugin.NotificationType.TrackChanged:
                        AddTrackContext(entry);
                        break;
                    case Plugin.NotificationType.PlayStateChanged:
                        AddPlayStateContext(entry);
                        break;
                    case Plugin.NotificationType.VolumeLevelChanged:
                        AddVolumeContext(entry);
                        break;
                    case Plugin.NotificationType.VolumeMuteChanged:
                        AddMuteContext(entry);
                        break;
                    case Plugin.NotificationType.PlayerRepeatChanged:
                        AddRepeatContext(entry);
                        break;
                    case Plugin.NotificationType.PlayerShuffleChanged:
                        AddShuffleContext(entry);
                        break;
                    case Plugin.NotificationType.PlayerEqualiserOnOffChanged:
                        AddEqContext(entry);
                        break;
                    case Plugin.NotificationType.PlayerScrobbleChanged:
                        AddScrobbleContext(entry);
                        break;
                    case Plugin.NotificationType.ReplayGainChanged:
                        AddReplayGainContext(entry);
                        break;
                    case Plugin.NotificationType.StopAfterCurrentChanged:
                        AddStopAfterCurrentContext(entry);
                        break;
                    case Plugin.NotificationType.AutoDjStarted:
                    case Plugin.NotificationType.AutoDjStopped:
                        AddAutoDjContext(entry);
                        break;
                    case Plugin.NotificationType.NowPlayingArtworkReady:
                        AddArtworkReadyContext(entry);
                        break;
                    case Plugin.NotificationType.NowPlayingLyricsReady:
                        AddLyricsReadyContext(entry);
                        break;

                    // Queue events
                    case Plugin.NotificationType.PlayingTracksChanged:
                    case Plugin.NotificationType.PlayingTracksQueueChanged:
#pragma warning disable CS0618
                    case Plugin.NotificationType.NowPlayingListChanged:
#pragma warning restore CS0618
                        AddQueueContext(entry);
                        break;
                    case Plugin.NotificationType.NowPlayingListEnded:
                        AddQueueEndedContext(entry);
                        break;

                    // Tags events
                    case Plugin.NotificationType.TagsChanging:
                        AddTagsChangingContext(entry);
                        break;
                    case Plugin.NotificationType.TagsChanged:
                        AddTagsChangedContext(entry);
                        break;
                    case Plugin.NotificationType.RatingChanging:
                        AddRatingChangingContext(entry);
                        break;
                    case Plugin.NotificationType.RatingChanged:
                        AddRatingContext(entry);
                        break;
                    case Plugin.NotificationType.PlayCountersChanged:
                        AddPlayCountersContext(entry);
                        break;

                    // Library events
                    case Plugin.NotificationType.FileDeleting:
                        AddFileDeletingContext(entry);
                        break;
                    case Plugin.NotificationType.FileDeleted:
                        AddFileDeletedContext(entry);
                        break;
                    case Plugin.NotificationType.LibrarySwitched:
                        AddLibrarySwitchedContext(entry);
                        break;
                    case Plugin.NotificationType.FileAddedToLibrary:
                        AddFileAddedContext(entry, "Library");
                        break;
                    case Plugin.NotificationType.FileAddedToInbox:
                        AddFileAddedContext(entry, "Inbox");
                        break;

                    // Playlist events
                    case Plugin.NotificationType.PlaylistCreated:
                        AddPlaylistContext(entry, "Created");
                        break;
                    case Plugin.NotificationType.PlaylistUpdated:
                        AddPlaylistContext(entry, "Updated");
                        break;
                    case Plugin.NotificationType.PlaylistDeleted:
                        AddPlaylistContext(entry, "Deleted");
                        break;

                    // Sync events
                    case Plugin.NotificationType.SynchCompleted:
                        AddSyncCompletedContext(entry);
                        break;

                    // Download events
                    case Plugin.NotificationType.DownloadCompleted:
                        AddDownloadCompletedContext(entry);
                        break;

                    // UI events
                    case Plugin.NotificationType.EmbedInPanel:
                        AddEmbedInPanelContext(entry);
                        break;
                    case Plugin.NotificationType.ApplicationWindowChanged:
                        AddApplicationWindowContext(entry);
                        break;

                    // System events
                    case Plugin.NotificationType.ScreenSaverActivating:
                        AddScreenSaverContext(entry);
                        break;
                }
            }
            catch (Exception ex)
            {
                entry.WithContext("ContextError", ex.Message);
            }
        }

        #region Context Helpers

        private void AddTrackContext(LogEntry entry)
        {
            // === Core Metadata (via GetFileTag) ===
            var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
            var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
            var album = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Album);
            var albumArtist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.AlbumArtist);
            var genre = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Genre);
            var year = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Year);
            var trackNo = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackNo);
            var trackCount = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackCount);
            var discNo = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.DiscNo);
            var discCount = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.DiscCount);
            var composer = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Composer);
            var rating = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);
            var ratingLove = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.RatingLove);
            var bpm = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.BeatsPerMin);
            var comment = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Comment);
            var mood = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Mood);
            var grouping = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Grouping);
            var publisher = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Publisher);
            var encoder = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Encoder);

            // === Audio Properties (via GetFileProperty) ===
            var duration = _mbApi.NowPlaying_GetDuration?.Invoke() ?? 0;
            var bitrate = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Bitrate);
            var format = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Format);
            var kind = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Kind);
            var sampleRate = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.SampleRate);
            var channels = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Channels);
            var fileSize = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Size);

            // === ReplayGain ===
            var replayGainTrack = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.ReplayGainTrack);
            var replayGainAlbum = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.ReplayGainAlbum);

            // === Library/Usage Statistics ===
            var dateAdded = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.DateAdded);
            var dateModified = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.DateModified);
            var lastPlayed = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.LastPlayed);
            var playCount = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.PlayCount);
            var skipCount = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.SkipCount);
            var status = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Status);

            // === Queue Position ===
            var queueIndex = _mbApi.NowPlayingList_GetCurrentIndex?.Invoke() ?? -1;
            var hasNextTrack = _mbApi.NowPlayingList_IsAnyFollowingTracks?.Invoke() ?? false;
            var hasPriorTrack = _mbApi.NowPlayingList_IsAnyPriorTracks?.Invoke() ?? false;

            // === Playback Position (for diagnosing early track changes) ===
            var position = _mbApi.Player_GetPosition?.Invoke() ?? 0;
            var positionPct = duration > 0 ? (position * 100.0 / duration) : 0;

            // === Player Mode ===
            var repeatMode = _mbApi.Player_GetRepeat?.Invoke() ?? Plugin.RepeatMode.None;
            var shuffleEnabled = _mbApi.Player_GetShuffle?.Invoke() ?? false;

            // === File Path ===
            var path = _mbApi.NowPlaying_GetFileUrl?.Invoke();

            // === Build Context Dictionary ===
            // Core Metadata
            entry.WithContext("Artist", artist ?? "")
                 .WithContext("AlbumArtist", albumArtist ?? "")
                 .WithContext("Title", title ?? "")
                 .WithContext("Album", album ?? "")
                 .WithContext("Genre", genre ?? "")
                 .WithContext("Year", year ?? "")
                 .WithContext("TrackNo", trackNo ?? "")
                 .WithContext("TrackCount", trackCount ?? "")
                 .WithContext("DiscNo", discNo ?? "")
                 .WithContext("DiscCount", discCount ?? "")
                 .WithContext("Composer", composer ?? "")
                 .WithContext("Rating", rating ?? "")
                 .WithContext("RatingLove", ratingLove ?? "")
                 .WithContext("BPM", bpm ?? "")
                 .WithContext("Comment", TruncateString(comment, 100))
                 .WithContext("Mood", mood ?? "")
                 .WithContext("Grouping", grouping ?? "")
                 .WithContext("Publisher", publisher ?? "")
                 .WithContext("Encoder", encoder ?? "")

                 // Audio Properties
                 .WithContext("Duration", duration)
                 .WithContext("DurationFormatted", FormatDuration(duration))
                 .WithContext("Bitrate", bitrate ?? "")
                 .WithContext("Format", format ?? "")
                 .WithContext("Kind", kind ?? "")
                 .WithContext("SampleRate", sampleRate ?? "")
                 .WithContext("Channels", channels ?? "")
                 .WithContext("FileSize", fileSize ?? "")
                 .WithContext("FileSizeFormatted", FormatFileSize(fileSize))

                 // ReplayGain
                 .WithContext("ReplayGainTrack", replayGainTrack ?? "")
                 .WithContext("ReplayGainAlbum", replayGainAlbum ?? "")

                 // Library Statistics
                 .WithContext("DateAdded", dateAdded ?? "")
                 .WithContext("DateModified", dateModified ?? "")
                 .WithContext("LastPlayed", lastPlayed ?? "")
                 .WithContext("PlayCount", playCount ?? "")
                 .WithContext("SkipCount", skipCount ?? "")
                 .WithContext("Status", status ?? "")

                 // Queue Context
                 .WithContext("QueueIndex", queueIndex)
                 .WithContext("HasNextTrack", hasNextTrack)
                 .WithContext("HasPriorTrack", hasPriorTrack)

                 // Playback Position (for diagnosing early track changes)
                 .WithContext("Position", position)
                 .WithContext("PositionFormatted", FormatDuration(position))
                 .WithContext("PositionPct", $"{positionPct:F1}%")

                 // Player Mode
                 .WithContext("RepeatMode", repeatMode.ToString())
                 .WithContext("ShuffleEnabled", shuffleEnabled)

                 // File Path
                 .WithContext("Path", path ?? "");
        }

        // Track previous volume for delta calculation
        private float _previousVolume = -1f;

        private void AddPlayStateContext(LogEntry entry)
        {
            // === Core Play State ===
            var state = _mbApi.Player_GetPlayState?.Invoke() ?? Plugin.PlayState.Undefined;
            var position = _mbApi.Player_GetPosition?.Invoke() ?? 0;
            var duration = _mbApi.NowPlaying_GetDuration?.Invoke() ?? 0;

            // === Volume and Mute ===
            var volume = _mbApi.Player_GetVolume?.Invoke() ?? 0f;
            var muted = _mbApi.Player_GetMute?.Invoke() ?? false;

            // === Player Mode Settings ===
            var shuffle = _mbApi.Player_GetShuffle?.Invoke() ?? false;
            var repeat = _mbApi.Player_GetRepeat?.Invoke() ?? Plugin.RepeatMode.None;
            var replayGainMode = _mbApi.Player_GetReplayGainMode?.Invoke() ?? Plugin.ReplayGainMode.Off;
            var crossfade = _mbApi.Player_GetCrossfade?.Invoke() ?? false;
            var autoDj = _mbApi.Player_GetAutoDjEnabled?.Invoke() ?? false;
            var stopAfterCurrent = _mbApi.Player_GetStopAfterCurrentEnabled?.Invoke() ?? false;
            var equalizer = _mbApi.Player_GetEqualiserEnabled?.Invoke() ?? false;
            var dsp = _mbApi.Player_GetDspEnabled?.Invoke() ?? false;
            var scrobble = _mbApi.Player_GetScrobbleEnabled?.Invoke() ?? false;

            // === Output Device Info (if API available) ===
            string activeDevice = null;
            int deviceCount = 0;
            try
            {
                if (_mbApi.Player_GetOutputDevices != null)
                {
                    string[] deviceNames = null;
                    _mbApi.Player_GetOutputDevices(out deviceNames, out activeDevice);
                    deviceCount = deviceNames?.Length ?? 0;
                }
            }
            catch { /* API may not be available in older versions */ }

            // === Current Track Audio Properties (when playing) ===
            string sampleRate = null;
            string channels = null;
            string format = null;
            string bitrate = null;
            if (state == Plugin.PlayState.Playing || state == Plugin.PlayState.Paused)
            {
                sampleRate = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.SampleRate);
                channels = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Channels);
                format = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Format);
                bitrate = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.Bitrate);
            }

            // === Build Context ===
            entry.WithContext("PlayState", state.ToString())
                 .WithContext("PlayStateValue", (int)state)
                 .WithContext("Position", position)
                 .WithContext("PositionFormatted", FormatDuration(position))
                 .WithContext("Duration", duration)
                 .WithContext("DurationFormatted", FormatDuration(duration))
                 .WithContext("ProgressPercent", duration > 0 ? Math.Round(position * 100.0 / duration, 1) : 0)

                 // Volume
                 .WithContext("Volume", volume)
                 .WithContext("VolumePercent", (int)(volume * 100))
                 .WithContext("Muted", muted)

                 // Player Modes
                 .WithContext("Shuffle", shuffle)
                 .WithContext("RepeatMode", repeat.ToString())
                 .WithContext("RepeatModeValue", (int)repeat)
                 .WithContext("ReplayGainMode", replayGainMode.ToString())
                 .WithContext("Crossfade", crossfade)
                 .WithContext("AutoDj", autoDj)
                 .WithContext("StopAfterCurrent", stopAfterCurrent)
                 .WithContext("EqualizerEnabled", equalizer)
                 .WithContext("DspEnabled", dsp)
                 .WithContext("ScrobbleEnabled", scrobble)

                 // Output Device
                 .WithContext("ActiveOutputDevice", activeDevice ?? "")
                 .WithContext("OutputDeviceCount", deviceCount);

            // Add audio properties when playing
            if (!string.IsNullOrEmpty(sampleRate))
                entry.WithContext("AudioSampleRate", sampleRate);
            if (!string.IsNullOrEmpty(channels))
                entry.WithContext("AudioChannels", channels);
            if (!string.IsNullOrEmpty(format))
                entry.WithContext("AudioFormat", format);
            if (!string.IsNullOrEmpty(bitrate))
                entry.WithContext("AudioBitrate", bitrate);
        }

        private void AddVolumeContext(LogEntry entry)
        {
            var currentVolume = _mbApi.Player_GetVolume?.Invoke() ?? 0f;

            entry.WithContext("Volume", currentVolume)
                 .WithContext("VolumePercent", (int)(currentVolume * 100));

            // Track before/after for volume changes
            if (_previousVolume >= 0)
            {
                var delta = currentVolume - _previousVolume;
                entry.WithContext("PreviousVolume", _previousVolume)
                     .WithContext("PreviousVolumePercent", (int)(_previousVolume * 100))
                     .WithContext("VolumeDelta", delta)
                     .WithContext("VolumeDeltaPercent", (int)(delta * 100))
                     .WithContext("VolumeDirection", delta > 0 ? "Up" : delta < 0 ? "Down" : "None");
            }

            _previousVolume = currentVolume;
        }

        private void AddMuteContext(LogEntry entry)
        {
            var muted = _mbApi.Player_GetMute?.Invoke() ?? false;
            entry.WithContext("Muted", muted);
        }

        private void AddRatingContext(LogEntry entry)
        {
            var rating = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);
            entry.WithContext("Rating", rating ?? "");
        }

        private void AddRepeatContext(LogEntry entry)
        {
            var repeat = _mbApi.Player_GetRepeat?.Invoke() ?? Plugin.RepeatMode.None;
            entry.WithContext("RepeatMode", repeat.ToString())
                 .WithContext("RepeatModeValue", (int)repeat);
        }

        private void AddShuffleContext(LogEntry entry)
        {
            var shuffle = _mbApi.Player_GetShuffle?.Invoke() ?? false;
            entry.WithContext("Shuffle", shuffle);
        }

        private void AddEqContext(LogEntry entry)
        {
            var enabled = _mbApi.Player_GetEqualiserEnabled?.Invoke() ?? false;
            entry.WithContext("EqualizerEnabled", enabled);
        }

        private void AddScrobbleContext(LogEntry entry)
        {
            var enabled = _mbApi.Player_GetScrobbleEnabled?.Invoke() ?? false;
            entry.WithContext("ScrobbleEnabled", enabled);
        }

        private void AddReplayGainContext(LogEntry entry)
        {
            var mode = _mbApi.Player_GetReplayGainMode?.Invoke() ?? Plugin.ReplayGainMode.Off;
            entry.WithContext("ReplayGainMode", mode.ToString())
                 .WithContext("ReplayGainModeValue", (int)mode);
        }

        private void AddStopAfterCurrentContext(LogEntry entry)
        {
            var enabled = _mbApi.Player_GetStopAfterCurrentEnabled?.Invoke() ?? false;
            entry.WithContext("StopAfterCurrent", enabled);
        }

        private void AddAutoDjContext(LogEntry entry)
        {
            var enabled = _mbApi.Player_GetAutoDjEnabled?.Invoke() ?? false;
            entry.WithContext("AutoDjEnabled", enabled);
        }

        private void AddQueueContext(LogEntry entry)
        {
            var currentIndex = _mbApi.NowPlayingList_GetCurrentIndex?.Invoke() ?? -1;
            var hasPrior = _mbApi.NowPlayingList_IsAnyPriorTracks?.Invoke() ?? false;
            var hasFollowing = _mbApi.NowPlayingList_IsAnyFollowingTracks?.Invoke() ?? false;

            entry.WithContext("CurrentIndex", currentIndex)
                 .WithContext("HasPriorTracks", hasPrior)
                 .WithContext("HasFollowingTracks", hasFollowing);
        }

        private void AddPlayCountersContext(LogEntry entry)
        {
            var playCount = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.PlayCount);
            var skipCount = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.SkipCount);
            var lastPlayed = _mbApi.NowPlaying_GetFileProperty?.Invoke(Plugin.FilePropertyType.LastPlayed);

            entry.WithContext("PlayCount", playCount ?? "")
                 .WithContext("SkipCount", skipCount ?? "")
                 .WithContext("LastPlayed", lastPlayed ?? "");
        }

        // Core events context
        private void AddPluginStartupContext(LogEntry entry)
        {
            entry.WithContext("InterfaceVersion", _mbApi.InterfaceVersion)
                 .WithContext("ApiRevision", _mbApi.ApiRevision)
                 .WithContext("MusicBeeVersion", _mbApi.MusicBeeVersion.ToString())
                 .WithContext("PluginStoragePath", _mbApi.Setting_GetPersistentStoragePath?.Invoke() ?? "")
                 .WithContext("CurrentSkin", _mbApi.Setting_GetSkin?.Invoke() ?? "");
        }

        private void AddMusicBeeStartedContext(LogEntry entry)
        {
            entry.WithContext("InterfaceVersion", _mbApi.InterfaceVersion)
                 .WithContext("ApiRevision", _mbApi.ApiRevision)
                 .WithContext("MusicBeeVersion", _mbApi.MusicBeeVersion.ToString());

            // Add current player state
            var playState = _mbApi.Player_GetPlayState?.Invoke() ?? Plugin.PlayState.Undefined;
            entry.WithContext("CurrentPlayState", playState.ToString());
        }

        private void AddShutdownContext(LogEntry entry)
        {
            entry.WithContext("ShutdownTime", DateTime.Now.ToString("o"))
                 .WithContext("TotalEntriesLogged", _buffer.Count);
        }

        // Player events context
        private void AddTrackChangingContext(LogEntry entry)
        {
            // Capture the "before" state - the track that is about to change
            var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
            var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
            var path = _mbApi.NowPlaying_GetFileUrl?.Invoke();
            var position = _mbApi.Player_GetPosition?.Invoke() ?? 0;
            var duration = _mbApi.NowPlaying_GetDuration?.Invoke() ?? 0;

            entry.WithContext("PreviousArtist", artist ?? "")
                 .WithContext("PreviousTitle", title ?? "")
                 .WithContext("PreviousPath", path ?? "")
                 .WithContext("PositionAtChange", position)
                 .WithContext("PositionFormatted", FormatDuration(position))
                 .WithContext("Duration", duration)
                 .WithContext("ProgressPercent", duration > 0 ? (position * 100.0 / duration) : 0);
        }

        private void AddArtworkReadyContext(LogEntry entry)
        {
            var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
            var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
            var album = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Album);
            var artworkUrl = _mbApi.NowPlaying_GetArtworkUrl?.Invoke();
            var downloadedArtworkUrl = _mbApi.NowPlaying_GetDownloadedArtworkUrl?.Invoke();

            entry.WithContext("Artist", artist ?? "")
                 .WithContext("Title", title ?? "")
                 .WithContext("Album", album ?? "")
                 .WithContext("ArtworkUrl", artworkUrl ?? "")
                 .WithContext("DownloadedArtworkUrl", downloadedArtworkUrl ?? "");

            // Try to get embedded artwork data info
            try
            {
                var artworkData = _mbApi.NowPlaying_GetArtwork?.Invoke();
                if (!string.IsNullOrEmpty(artworkData))
                {
                    entry.WithContext("HasEmbeddedArtwork", true)
                         .WithContext("ArtworkDataLength", artworkData.Length);
                }
                else
                {
                    entry.WithContext("HasEmbeddedArtwork", false);
                }
            }
            catch { }

            // Try to get artist pictures
            try
            {
                if (_mbApi.NowPlaying_GetArtistPictureUrls != null)
                {
                    string[] urls = null;
                    if (_mbApi.NowPlaying_GetArtistPictureUrls(true, out urls) && urls?.Length > 0)
                    {
                        entry.WithContext("ArtistPictureCount", urls.Length);
                        if (urls.Length > 0)
                            entry.WithContext("ArtistPictureUrl1", TruncateString(urls[0], 200));
                    }
                }
            }
            catch { }

            entry.WithContext("ArtworkReadyTime", DateTime.Now.ToString("o"));
        }

        private void AddLyricsReadyContext(LogEntry entry)
        {
            var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
            var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
            var hasLyrics = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.HasLyrics);

            entry.WithContext("Artist", artist ?? "")
                 .WithContext("Title", title ?? "")
                 .WithContext("HasLyrics", hasLyrics ?? "");
        }

        // Queue events context
        private void AddQueueEndedContext(LogEntry entry)
        {
            var currentIndex = _mbApi.NowPlayingList_GetCurrentIndex?.Invoke() ?? -1;
            entry.WithContext("FinalIndex", currentIndex)
                 .WithContext("QueueCompleted", true);
        }

        // Tags events context
        private void AddTagsChangingContext(LogEntry entry)
        {
            // Capture "before" state using Pending_* API when available
            var pendingUrl = _mbApi.Pending_GetFileUrl?.Invoke();
            if (!string.IsNullOrEmpty(pendingUrl))
            {
                var artist = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
                var title = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
                var album = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Album);
                var rating = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);

                entry.WithContext("PendingFile", pendingUrl)
                     .WithContext("BeforeArtist", artist ?? "")
                     .WithContext("BeforeTitle", title ?? "")
                     .WithContext("BeforeAlbum", album ?? "")
                     .WithContext("BeforeRating", rating ?? "");
            }
            else
            {
                // Fall back to current track
                var path = _mbApi.NowPlaying_GetFileUrl?.Invoke();
                entry.WithContext("TargetFile", path ?? "");
            }
        }

        private void AddTagsChangedContext(LogEntry entry)
        {
            // After tags changed, get the current state
            var path = _mbApi.NowPlaying_GetFileUrl?.Invoke();
            var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
            var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
            var album = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Album);

            entry.WithContext("File", path ?? "")
                 .WithContext("Artist", artist ?? "")
                 .WithContext("Title", title ?? "")
                 .WithContext("Album", album ?? "");
        }

        private void AddRatingChangingContext(LogEntry entry)
        {
            // Capture "before" rating state
            var pendingUrl = _mbApi.Pending_GetFileUrl?.Invoke();
            if (!string.IsNullOrEmpty(pendingUrl))
            {
                var rating = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);
                entry.WithContext("PendingFile", pendingUrl)
                     .WithContext("BeforeRating", rating ?? "");
            }
            else
            {
                var rating = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);
                entry.WithContext("BeforeRating", rating ?? "");
            }
        }

        // Library events context
        private void AddFileDeletingContext(LogEntry entry)
        {
            var pendingUrl = _mbApi.Pending_GetFileUrl?.Invoke();
            if (!string.IsNullOrEmpty(pendingUrl))
            {
                var artist = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
                var title = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
                var size = _mbApi.Pending_GetFileProperty?.Invoke(Plugin.FilePropertyType.Size);

                entry.WithContext("DeletingFile", pendingUrl)
                     .WithContext("Artist", artist ?? "")
                     .WithContext("Title", title ?? "")
                     .WithContext("FileSize", size ?? "");
            }
        }

        private void AddFileDeletedContext(LogEntry entry)
        {
            // File is already deleted, limited info available
            entry.WithContext("DeletionCompleted", true)
                 .WithContext("DeletionTime", DateTime.Now.ToString("o"));
        }

        private void AddLibrarySwitchedContext(LogEntry entry)
        {
            var storagePath = _mbApi.Setting_GetPersistentStoragePath?.Invoke();
            entry.WithContext("NewLibraryPath", storagePath ?? "")
                 .WithContext("SwitchTime", DateTime.Now.ToString("o"));
        }

        private void AddFileAddedContext(LogEntry entry, string destination)
        {
            var pendingUrl = _mbApi.Pending_GetFileUrl?.Invoke();
            if (!string.IsNullOrEmpty(pendingUrl))
            {
                var artist = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
                var title = _mbApi.Pending_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
                var format = _mbApi.Pending_GetFileProperty?.Invoke(Plugin.FilePropertyType.Format);

                entry.WithContext("AddedFile", pendingUrl)
                     .WithContext("Artist", artist ?? "")
                     .WithContext("Title", title ?? "")
                     .WithContext("Format", format ?? "")
                     .WithContext("Destination", destination);
            }
            else
            {
                entry.WithContext("Destination", destination);
            }
        }

        // Playlist events context - Track for playlist enrichment
        private string _lastKnownPlaylistUrl;

        private void AddPlaylistContext(LogEntry entry, string action)
        {
            entry.WithContext("PlaylistAction", action)
                 .WithContext("ActionTime", DateTime.Now.ToString("o"));

            // Try to get more context about playlists
            try
            {
                // Query all playlists to get counts and details
                if (_mbApi.Playlist_QueryPlaylists?.Invoke() == true)
                {
                    var playlists = new List<PlaylistInfo>();
                    string playlistUrl;
                    while ((playlistUrl = _mbApi.Playlist_QueryGetNextPlaylist?.Invoke()) != null)
                    {
                        var info = new PlaylistInfo
                        {
                            Url = playlistUrl,
                            Name = _mbApi.Playlist_GetName?.Invoke(playlistUrl),
                            Type = _mbApi.Playlist_GetType?.Invoke(playlistUrl) ?? Plugin.PlaylistFormat.Unknown
                        };

                        // Get file count
                        if (_mbApi.Playlist_QueryFilesEx?.Invoke(playlistUrl, out string[] files) == true)
                        {
                            info.FileCount = files?.Length ?? 0;
                        }

                        playlists.Add(info);
                    }

                    // Add summary info
                    entry.WithContext("TotalPlaylistCount", playlists.Count);

                    // For created action, the newest playlist is likely the one just created
                    if (action == "Created" && playlists.Count > 0)
                    {
                        var newest = playlists[playlists.Count - 1];
                        AddPlaylistDetails(entry, newest, "Created");
                        _lastKnownPlaylistUrl = newest.Url;
                    }
                    else if (action == "Updated" && !string.IsNullOrEmpty(_lastKnownPlaylistUrl))
                    {
                        var updated = playlists.Find(p => p.Url == _lastKnownPlaylistUrl);
                        if (updated != null)
                        {
                            AddPlaylistDetails(entry, updated, "Updated");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                entry.WithContext("PlaylistQueryError", ex.Message);
            }
        }

        private void AddPlaylistDetails(LogEntry entry, PlaylistInfo info, string prefix)
        {
            entry.WithContext($"{prefix}PlaylistName", info.Name ?? "Unknown")
                 .WithContext($"{prefix}PlaylistPath", info.Url ?? "")
                 .WithContext($"{prefix}PlaylistType", info.Type.ToString())
                 .WithContext($"{prefix}PlaylistFileCount", info.FileCount);
        }

        private class PlaylistInfo
        {
            public string Url { get; set; }
            public string Name { get; set; }
            public Plugin.PlaylistFormat Type { get; set; }
            public int FileCount { get; set; }
        }

        // Sync events context
        private void AddSyncCompletedContext(LogEntry entry)
        {
            var now = DateTime.Now;
            entry.WithContext("SyncCompletedTime", now.ToString("o"))
                 .WithContext("SyncStatus", "Completed");

            // If we have a SyncTracker, get rich session data
            if (SyncTracker != null)
            {
                var summary = SyncTracker.GetSessionSummary();

                entry.WithContext("SyncDurationMs", summary.DurationMs)
                     .WithContext("SyncDurationFormatted", FormatDuration((int)summary.DurationMs))
                     .WithContext("FilesSynced", summary.FilesSynced)
                     .WithContext("FilesDeleted", summary.FilesDeleted)
                     .WithContext("SyncErrors", summary.Errors)
                     .WithContext("DeviceName", summary.DeviceName ?? "Unknown")
                     .WithContext("ActiveOperationsAtEnd", summary.ActiveOperations);

                if (summary.Errors > 0)
                {
                    var failed = SyncTracker.GetFailedOperations();
                    if (failed.Count > 0)
                    {
                        var failedFiles = string.Join("; ",
                            failed.Take(5).Select(f => f.FileName ?? System.IO.Path.GetFileName(f.SourceFile)));
                        entry.WithContext("FailedFiles", failedFiles);
                        if (failed.Count > 5)
                            entry.WithContext("AdditionalFailures", failed.Count - 5);
                    }
                }

                // Reset for next sync session
                SyncTracker.Reset();
            }
        }

        // Download events context
        private void AddDownloadCompletedContext(LogEntry entry)
        {
            entry.WithContext("DownloadCompletedTime", DateTime.Now.ToString("o"))
                 .WithContext("DownloadStatus", "Completed");
        }

        // UI events context
        private void AddEmbedInPanelContext(LogEntry entry)
        {
            // Get bounds for all dock types
            var dockTypes = new[]
            {
                Plugin.PluginPanelDock.ApplicationWindow,
                Plugin.PluginPanelDock.TrackAndArtistPanel,
                Plugin.PluginPanelDock.MainPanel
            };

            foreach (var dock in dockTypes)
            {
                try
                {
                    var bounds = _mbApi.MB_GetPanelBounds?.Invoke(dock);
                    if (bounds.HasValue && bounds.Value.Width > 0)
                    {
                        var dockName = dock.ToString();
                        entry.WithContext($"Panel_{dockName}_X", bounds.Value.X)
                             .WithContext($"Panel_{dockName}_Y", bounds.Value.Y)
                             .WithContext($"Panel_{dockName}_Width", bounds.Value.Width)
                             .WithContext($"Panel_{dockName}_Height", bounds.Value.Height);
                    }
                }
                catch { }
            }

            // Get main window handle for context
            try
            {
                var hwnd = _mbApi.MB_GetWindowHandle?.Invoke();
                if (hwnd.HasValue && hwnd.Value != IntPtr.Zero)
                {
                    entry.WithContext("MainWindowHandle", hwnd.Value.ToInt64());
                }
            }
            catch { }

            entry.WithContext("EmbedTime", DateTime.Now.ToString("o"));
        }

        private void AddApplicationWindowContext(LogEntry entry)
        {
            var hwnd = _mbApi.MB_GetWindowHandle?.Invoke();
            if (hwnd == null || hwnd == IntPtr.Zero)
            {
                entry.WithContext("WindowHandle", 0)
                     .WithContext("ChangeTime", DateTime.Now.ToString("o"));
                return;
            }

            entry.WithContext("WindowHandle", hwnd.Value.ToInt64());

            // Use SpyHelper for rich Win32 details if available
            try
            {
                var controlInfo = SpyHelper.GetControlInfo(hwnd.Value);
                if (controlInfo != null)
                {
                    entry.WithContext("WindowBounds_X", controlInfo.ScreenBounds.X)
                         .WithContext("WindowBounds_Y", controlInfo.ScreenBounds.Y)
                         .WithContext("WindowBounds_Width", controlInfo.ScreenBounds.Width)
                         .WithContext("WindowBounds_Height", controlInfo.ScreenBounds.Height)
                         .WithContext("WindowVisible", controlInfo.IsVisible)
                         .WithContext("WindowEnabled", controlInfo.IsEnabled)
                         .WithContext("WindowStyle", $"0x{controlInfo.Style:X8}")
                         .WithContext("WindowExStyle", $"0x{controlInfo.ExStyle:X8}")
                         .WithContext("WindowClassName", controlInfo.ClassName ?? "")
                         .WithContext("WindowText", TruncateString(controlInfo.WindowText, 100) ?? "");

                    // Detect minimize/maximize from style
                    const int WS_MINIMIZE = 0x20000000;
                    const int WS_MAXIMIZE = 0x01000000;
                    bool isMinimized = (controlInfo.Style & WS_MINIMIZE) != 0;
                    bool isMaximized = (controlInfo.Style & WS_MAXIMIZE) != 0;
                    entry.WithContext("IsMinimized", isMinimized)
                         .WithContext("IsMaximized", isMaximized);
                }
            }
            catch { }

            entry.WithContext("ChangeTime", DateTime.Now.ToString("o"));
        }

        // System events context
        private void AddScreenSaverContext(LogEntry entry)
        {
            var playState = _mbApi.Player_GetPlayState?.Invoke() ?? Plugin.PlayState.Undefined;
            entry.WithContext("CurrentPlayState", playState.ToString())
                 .WithContext("ActivationTime", DateTime.Now.ToString("o"));
        }

        #endregion

        #region Message Helpers

        private string GetTrackChangedMessage()
        {
            try
            {
                var artist = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Artist);
                var title = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.TrackTitle);
                if (!string.IsNullOrEmpty(artist) && !string.IsNullOrEmpty(title))
                {
                    return $"Now playing: {artist} - {title}";
                }
                return "Track changed";
            }
            catch
            {
                return "Track changed";
            }
        }

        private string GetPlayStateMessage()
        {
            try
            {
                var state = _mbApi.Player_GetPlayState?.Invoke() ?? Plugin.PlayState.Undefined;
                return $"Play state changed to: {state}";
            }
            catch
            {
                return "Play state changed";
            }
        }

        private string GetMuteMessage()
        {
            try
            {
                var muted = _mbApi.Player_GetMute?.Invoke() ?? false;
                return muted ? "Audio muted" : "Audio unmuted";
            }
            catch
            {
                return "Mute state changed";
            }
        }

        private string GetVolumeMessage()
        {
            try
            {
                var volume = _mbApi.Player_GetVolume?.Invoke() ?? 0f;
                return $"Volume changed to: {(int)(volume * 100)}%";
            }
            catch
            {
                return "Volume level changed";
            }
        }

        private string GetRatingMessage()
        {
            try
            {
                var rating = _mbApi.NowPlaying_GetFileTag?.Invoke(Plugin.MetaDataType.Rating);
                return $"Rating changed to: {rating ?? "none"}";
            }
            catch
            {
                return "Rating changed";
            }
        }

        private string GetRepeatMessage()
        {
            try
            {
                var repeat = _mbApi.Player_GetRepeat?.Invoke() ?? Plugin.RepeatMode.None;
                return $"Repeat mode changed to: {repeat}";
            }
            catch
            {
                return "Repeat mode changed";
            }
        }

        private string GetShuffleMessage()
        {
            try
            {
                var shuffle = _mbApi.Player_GetShuffle?.Invoke() ?? false;
                return shuffle ? "Shuffle enabled" : "Shuffle disabled";
            }
            catch
            {
                return "Shuffle state changed";
            }
        }

        private string GetEqMessage()
        {
            try
            {
                var enabled = _mbApi.Player_GetEqualiserEnabled?.Invoke() ?? false;
                return enabled ? "Equalizer enabled" : "Equalizer disabled";
            }
            catch
            {
                return "Equalizer state changed";
            }
        }

        private string GetScrobbleMessage()
        {
            try
            {
                var enabled = _mbApi.Player_GetScrobbleEnabled?.Invoke() ?? false;
                return enabled ? "Scrobbling enabled" : "Scrobbling disabled";
            }
            catch
            {
                return "Scrobble state changed";
            }
        }

        private string GetReplayGainMessage()
        {
            try
            {
                var mode = _mbApi.Player_GetReplayGainMode?.Invoke() ?? Plugin.ReplayGainMode.Off;
                return $"ReplayGain mode changed to: {mode}";
            }
            catch
            {
                return "ReplayGain mode changed";
            }
        }

        private string GetStopAfterCurrentMessage()
        {
            try
            {
                var enabled = _mbApi.Player_GetStopAfterCurrentEnabled?.Invoke() ?? false;
                return enabled ? "Stop after current enabled" : "Stop after current disabled";
            }
            catch
            {
                return "Stop after current changed";
            }
        }

        #endregion

        #region Utility Methods

        private string FormatDuration(int milliseconds)
        {
            var ts = TimeSpan.FromMilliseconds(milliseconds);
            if (ts.Hours > 0)
            {
                return $"{ts.Hours}:{ts.Minutes:D2}:{ts.Seconds:D2}";
            }
            return $"{ts.Minutes}:{ts.Seconds:D2}";
        }

        /// <summary>
        /// Formats file size as human-readable string.
        /// </summary>
        private string FormatFileSize(string sizeString)
        {
            if (string.IsNullOrEmpty(sizeString) || !long.TryParse(sizeString, out long bytes))
                return "";

            if (bytes >= 1024L * 1024 * 1024)
                return $"{bytes / (1024.0 * 1024 * 1024):F1} GB";
            if (bytes >= 1024 * 1024)
                return $"{bytes / (1024.0 * 1024):F1} MB";
            if (bytes >= 1024)
                return $"{bytes / 1024.0:F1} KB";
            return $"{bytes} B";
        }

        /// <summary>
        /// Truncates a string to the specified maximum length.
        /// </summary>
        private string TruncateString(string value, int maxLength)
        {
            if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
                return value ?? "";
            return value.Substring(0, maxLength) + "...";
        }

        private void WriteToNLog(LogEntry entry)
        {
            var logEvent = new LogEventInfo(
                GetNLogLevel(entry.Level),
                "Clouseau.Events",
                entry.Message
            );

            // Core entry fields
            logEvent.Properties["Timestamp"] = entry.Timestamp.ToString("o"); // ISO 8601
            logEvent.Properties["TimestampMs"] = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff");
            logEvent.Properties["Category"] = entry.Category;
            logEvent.Properties["EventType"] = entry.EventType;
            logEvent.Properties["EventTypeValue"] = entry.EventTypeValue;
            logEvent.Properties["Level"] = entry.Level;
            logEvent.Properties["SourceFile"] = entry.SourceFile ?? "";
            logEvent.Properties["SequenceNumber"] = entry.SequenceNumber;

            // All context data - flattened into properties for JSON logging
            if (entry.Context != null)
            {
                foreach (var kvp in entry.Context)
                {
                    logEvent.Properties[kvp.Key] = kvp.Value;
                }
            }

            NLogger.Log(logEvent);
        }

        private NLog.LogLevel GetNLogLevel(string level)
        {
            switch (level?.ToLowerInvariant())
            {
                case "debug":
                    return NLog.LogLevel.Debug;
                case "warn":
                case "warning":
                    return NLog.LogLevel.Warn;
                case "error":
                    return NLog.LogLevel.Error;
                case "info":
                default:
                    return NLog.LogLevel.Info;
            }
        }

        protected virtual void OnLogEntryAdded(LogEntry entry)
        {
            LogEntryAdded?.Invoke(this, entry);
        }

        #endregion

        #region IDisposable

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

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _buffer?.Dispose();
                }
                _disposed = true;
            }
        }

        #endregion
    }
}
