// ============================================================================
// mb_clouseau - Artwork Pipeline Tracker
// Uncovering clues with MusicBee Clouseau
//
// Tracks artwork requests, loads, timing, and cache behavior
// ============================================================================

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

namespace MusicBeePlugin.Clouseau.Introspection
{
    /// <summary>
    /// Tracks artwork requests, loads, and timing for performance analysis.
    /// </summary>
    public class ArtworkPipeline : IDisposable
    {
        private readonly ConcurrentDictionary<long, ArtworkRequest> _activeRequests;
        private readonly ConcurrentQueue<ArtworkRequest> _completedRequests;
        private const int MaxHistorySize = 200;
        private long _requestCounter;
        private bool _disposed;

        // Statistics
        private long _totalRequests;
        private long _successfulRequests;
        private long _failedRequests;
        private long _totalLoadTimeMs;
        private long _totalBytesLoaded;

        /// <summary>
        /// Event raised when an artwork request completes.
        /// </summary>
        public event EventHandler<ArtworkRequest> ArtworkLoaded;

        public ArtworkPipeline()
        {
            _activeRequests = new ConcurrentDictionary<long, ArtworkRequest>();
            _completedRequests = new ConcurrentQueue<ArtworkRequest>();
        }

        /// <summary>
        /// Gets the count of active artwork requests.
        /// </summary>
        public int ActiveRequestCount => _activeRequests.Count;

        /// <summary>
        /// Gets total artwork requests.
        /// </summary>
        public long TotalRequests => _totalRequests;

        /// <summary>
        /// Gets successful artwork requests.
        /// </summary>
        public long SuccessfulRequests => _successfulRequests;

        /// <summary>
        /// Gets average load time in milliseconds.
        /// </summary>
        public double AverageLoadTimeMs => _successfulRequests > 0
            ? (double)_totalLoadTimeMs / _successfulRequests
            : 0;

        /// <summary>
        /// Starts tracking an artwork request.
        /// </summary>
        /// <returns>Request ID for completion tracking.</returns>
        public long StartRequest(ArtworkRequestType type, string sourceFile = null, string url = null)
        {
            var requestId = Interlocked.Increment(ref _requestCounter);
            Interlocked.Increment(ref _totalRequests);

            var request = new ArtworkRequest
            {
                RequestId = requestId,
                RequestTime = DateTime.Now,
                SourceFile = sourceFile,
                Type = type,
                RequestedUrl = url
            };

            _activeRequests[requestId] = request;

            LogManager.Core.Trace($"Artwork request started: ID={requestId}, Type={type}");
            return requestId;
        }

        /// <summary>
        /// Completes an artwork request.
        /// </summary>
        public void CompleteRequest(long requestId, bool success, int? width = null,
            int? height = null, long? sizeBytes = null, string errorMessage = null)
        {
            if (_activeRequests.TryRemove(requestId, out var request))
            {
                request.CompletionTime = DateTime.Now;
                request.LoadDuration = request.CompletionTime.Value - request.RequestTime;
                request.Success = success;
                request.ImageWidth = width;
                request.ImageHeight = height;
                request.ImageSizeBytes = sizeBytes;
                request.ErrorMessage = errorMessage;

                if (success)
                {
                    Interlocked.Increment(ref _successfulRequests);
                    Interlocked.Add(ref _totalLoadTimeMs, (long)request.LoadDuration.Value.TotalMilliseconds);
                    if (sizeBytes.HasValue)
                        Interlocked.Add(ref _totalBytesLoaded, sizeBytes.Value);
                }
                else
                {
                    Interlocked.Increment(ref _failedRequests);
                }

                EnqueueCompleted(request);

                LogManager.Core.Trace($"Artwork request completed: ID={requestId}, " +
                    $"Success={success}, Duration={request.LoadDuration?.TotalMilliseconds:F0}ms");

                OnArtworkLoaded(request);
            }
        }

        /// <summary>
        /// Records a completed artwork request directly (for sync API calls).
        /// </summary>
        public void RecordCompletedRequest(ArtworkRequestType type, string sourceFile,
            long durationMs, bool success, int? width = null, int? height = null,
            long? sizeBytes = null, Plugin.PictureLocations? locations = null)
        {
            var requestId = Interlocked.Increment(ref _requestCounter);
            Interlocked.Increment(ref _totalRequests);

            var request = new ArtworkRequest
            {
                RequestId = requestId,
                RequestTime = DateTime.Now.AddMilliseconds(-durationMs),
                CompletionTime = DateTime.Now,
                SourceFile = sourceFile,
                Type = type,
                LoadDuration = TimeSpan.FromMilliseconds(durationMs),
                Success = success,
                ImageWidth = width,
                ImageHeight = height,
                ImageSizeBytes = sizeBytes,
                Locations = locations
            };

            if (success)
            {
                Interlocked.Increment(ref _successfulRequests);
                Interlocked.Add(ref _totalLoadTimeMs, durationMs);
                if (sizeBytes.HasValue)
                    Interlocked.Add(ref _totalBytesLoaded, sizeBytes.Value);
            }
            else
            {
                Interlocked.Increment(ref _failedRequests);
            }

            EnqueueCompleted(request);
            OnArtworkLoaded(request);
        }

        /// <summary>
        /// Gets recent completed requests.
        /// </summary>
        public IReadOnlyList<ArtworkRequest> GetRecentRequests(int count = 50)
        {
            var all = _completedRequests.ToArray();
            return all.Skip(Math.Max(0, all.Length - count)).ToList();
        }

        /// <summary>
        /// Gets active (in-flight) requests.
        /// </summary>
        public IReadOnlyList<ArtworkRequest> GetActiveRequests()
        {
            return _activeRequests.Values.ToList();
        }

        /// <summary>
        /// Gets artwork pipeline statistics.
        /// </summary>
        public ArtworkPipelineStatistics GetStatistics()
        {
            var recent = _completedRequests.ToArray();
            var recentSuccessful = recent.Where(r => r.Success == true).ToList();

            return new ArtworkPipelineStatistics
            {
                TotalRequests = _totalRequests,
                SuccessfulRequests = _successfulRequests,
                FailedRequests = _failedRequests,
                ActiveRequests = _activeRequests.Count,
                AverageLoadTimeMs = AverageLoadTimeMs,
                TotalBytesLoaded = _totalBytesLoaded,
                RequestsByType = recent.GroupBy(r => r.Type)
                    .ToDictionary(g => g.Key, g => g.Count()),
                RecentAverageLoadTimeMs = recentSuccessful.Count > 0
                    ? recentSuccessful.Average(r => r.LoadDuration?.TotalMilliseconds ?? 0)
                    : 0,
                LongestLoadTimeMs = recentSuccessful.Count > 0
                    ? recentSuccessful.Max(r => r.LoadDuration?.TotalMilliseconds ?? 0)
                    : 0
            };
        }

        private void EnqueueCompleted(ArtworkRequest request)
        {
            _completedRequests.Enqueue(request);
            while (_completedRequests.Count > MaxHistorySize)
            {
                _completedRequests.TryDequeue(out _);
            }
        }

        protected virtual void OnArtworkLoaded(ArtworkRequest request)
            => ArtworkLoaded?.Invoke(this, request);

        public void Dispose()
        {
            if (!_disposed)
            {
                _activeRequests.Clear();
                _disposed = true;
            }
        }
    }

    /// <summary>
    /// Type of artwork request.
    /// </summary>
    public enum ArtworkRequestType
    {
        NowPlayingArtwork,
        NowPlayingArtworkUrl,
        NowPlayingDownloadedArtwork,
        LibraryArtwork,
        LibraryArtworkEx,
        ArtistPicture,
        ArtistPictureUrl,
        ArtistPictureThumb
    }

    /// <summary>
    /// Represents an artwork request.
    /// </summary>
    public class ArtworkRequest
    {
        public long RequestId { get; set; }
        public DateTime RequestTime { get; set; }
        public DateTime? CompletionTime { get; set; }
        public string SourceFile { get; set; }
        public ArtworkRequestType Type { get; set; }
        public string RequestedUrl { get; set; }
        public TimeSpan? LoadDuration { get; set; }
        public bool? Success { get; set; }
        public int? ImageWidth { get; set; }
        public int? ImageHeight { get; set; }
        public long? ImageSizeBytes { get; set; }
        public Plugin.PictureLocations? Locations { get; set; }
        public string ErrorMessage { get; set; }

        public bool IsActive => !CompletionTime.HasValue;
    }

    /// <summary>
    /// Artwork pipeline statistics.
    /// </summary>
    public class ArtworkPipelineStatistics
    {
        public long TotalRequests { get; set; }
        public long SuccessfulRequests { get; set; }
        public long FailedRequests { get; set; }
        public int ActiveRequests { get; set; }
        public double AverageLoadTimeMs { get; set; }
        public long TotalBytesLoaded { get; set; }
        public Dictionary<ArtworkRequestType, int> RequestsByType { get; set; }
        public double RecentAverageLoadTimeMs { get; set; }
        public double LongestLoadTimeMs { get; set; }

        public double SuccessRate => TotalRequests > 0
            ? (double)SuccessfulRequests / TotalRequests * 100
            : 0;
    }
}
