// ============================================================================
// mb_clouseau - Metrics Collector Orchestrator
// Uncovering clues with MusicBee Clouseau
//
// Central orchestrator for all metrics collection:
// - Aggregates metrics from all collectors
// - Runs on background timer (5 second intervals)
// - Logs summary every 30 seconds
// - Tracks historical data for trend analysis
// - Detects memory leaks and handle leaks
// ============================================================================

using System;
using System.Collections.Concurrent;
using System.Threading;
using NLog;

namespace MusicBeePlugin.Clouseau.Metrics
{
    /// <summary>
    /// Central orchestrator for all metrics collection.
    /// Manages collection timing, aggregation, logging, and trend analysis.
    /// </summary>
    public class MetricsCollector : IDisposable
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private static readonly Logger MetricsLogger = LogManager.GetLogger("Clouseau.Metrics");

        // Collection intervals
        private const int CollectionIntervalMs = 5000;     // Collect every 5 seconds
        private const int LogSummaryIntervalMs = 30000;    // Log summary every 30 seconds
        private const int LogSummaryCollections = LogSummaryIntervalMs / CollectionIntervalMs; // 6 collections per summary

        // Sub-collectors
        private readonly SystemMetrics _systemMetrics;
        private readonly ProcessMetrics _processMetrics;
        private readonly ClrMetrics _clrMetrics;
        private readonly AudioMetrics _audioMetrics;

        // Timer for background collection
        private Timer _collectionTimer;

        // Current and historical snapshots
        private MetricsSnapshot _currentSnapshot;
        private readonly object _snapshotLock = new object();

        // Historical data for trend analysis
        private const int MaxHistorySize = 720; // 1 hour at 5s intervals
        private readonly ConcurrentQueue<MetricsSnapshot> _history = new ConcurrentQueue<MetricsSnapshot>();

        // Collection tracking
        private int _collectionCount;
        private DateTime _startTime;
        private DateTime _lastLogTime;

        // State
        private bool _isRunning;
        private bool _disposed;

        /// <summary>
        /// Event raised when a new metrics snapshot is available.
        /// </summary>
        public event EventHandler<MetricsSnapshot> SnapshotCollected;

        /// <summary>
        /// Event raised when a leak is detected.
        /// </summary>
        public event EventHandler<LeakDetectedEventArgs> LeakDetected;

        /// <summary>
        /// Gets whether the collector is currently running.
        /// </summary>
        public bool IsRunning => _isRunning;

        /// <summary>
        /// Gets the number of collections performed.
        /// </summary>
        public int CollectionCount => _collectionCount;

        /// <summary>
        /// Gets how long the collector has been running.
        /// </summary>
        public TimeSpan Uptime => _isRunning ? DateTime.Now - _startTime : TimeSpan.Zero;

        /// <summary>
        /// Gets the system metrics collector.
        /// </summary>
        public SystemMetrics SystemMetrics => _systemMetrics;

        /// <summary>
        /// Gets the process metrics collector.
        /// </summary>
        public ProcessMetrics ProcessMetrics => _processMetrics;

        /// <summary>
        /// Gets the CLR metrics collector.
        /// </summary>
        public ClrMetrics ClrMetrics => _clrMetrics;

        /// <summary>
        /// Gets the audio metrics collector.
        /// </summary>
        public AudioMetrics AudioMetrics => _audioMetrics;

        /// <summary>
        /// Creates a new MetricsCollector instance.
        /// </summary>
        public MetricsCollector()
        {
            _systemMetrics = new SystemMetrics();
            _processMetrics = new ProcessMetrics();
            _clrMetrics = new ClrMetrics();
            _audioMetrics = new AudioMetrics();
        }

        /// <summary>
        /// Starts the metrics collection background timer.
        /// </summary>
        public void Start()
        {
            if (_isRunning)
            {
                Logger.Warn("MetricsCollector.Start() called but already running");
                return;
            }

            try
            {
                Logger.Info("Starting MetricsCollector...");

                // Initialize all sub-collectors
                _systemMetrics.Initialize();
                _processMetrics.Initialize();
                _clrMetrics.Initialize();
                _audioMetrics.Initialize();

                // Record start time
                _startTime = DateTime.Now;
                _lastLogTime = _startTime;
                _collectionCount = 0;

                // Perform initial collection
                CollectNow();

                // Start the timer
                _collectionTimer = new Timer(
                    OnTimerTick,
                    null,
                    CollectionIntervalMs,
                    CollectionIntervalMs);

                _isRunning = true;
                Logger.Info("MetricsCollector started. Collection interval: {0}ms, Log interval: {1}ms",
                    CollectionIntervalMs, LogSummaryIntervalMs);

                // Log initial snapshot
                LogMetricsSummary();
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Failed to start MetricsCollector");
                Stop();
            }
        }

        /// <summary>
        /// Stops the metrics collection.
        /// </summary>
        public void Stop()
        {
            if (!_isRunning)
                return;

            try
            {
                Logger.Info("Stopping MetricsCollector...");

                _isRunning = false;

                // Stop the timer
                _collectionTimer?.Change(Timeout.Infinite, Timeout.Infinite);
                _collectionTimer?.Dispose();
                _collectionTimer = null;

                // Log final summary
                Logger.Info("MetricsCollector stopped. Total collections: {0}, Uptime: {1}",
                    _collectionCount, DateTime.Now - _startTime);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error stopping MetricsCollector");
            }
        }

        /// <summary>
        /// Timer callback - performs collection on background thread.
        /// </summary>
        private void OnTimerTick(object state)
        {
            if (!_isRunning)
                return;

            try
            {
                CollectNow();

                // Check if we should log a summary
                var timeSinceLastLog = DateTime.Now - _lastLogTime;
                if (timeSinceLastLog.TotalMilliseconds >= LogSummaryIntervalMs)
                {
                    LogMetricsSummary();
                    _lastLogTime = DateTime.Now;
                }
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error in metrics collection timer tick");
            }
        }

        /// <summary>
        /// Performs an immediate metrics collection.
        /// </summary>
        public MetricsSnapshot CollectNow()
        {
            var snapshot = new MetricsSnapshot
            {
                Timestamp = DateTime.Now
            };

            try
            {
                // Collect from all sources
                _systemMetrics.Collect(snapshot);
                _processMetrics.Collect(snapshot);
                _clrMetrics.Collect(snapshot);
                _audioMetrics.Collect(snapshot);

                // Update current snapshot (thread-safe)
                lock (_snapshotLock)
                {
                    _currentSnapshot = snapshot;
                }

                // Add to history
                AddToHistory(snapshot);

                _collectionCount++;

                // Fire event
                SnapshotCollected?.Invoke(this, snapshot);

                // Check for leaks and fire alerts
                CheckForLeaks(snapshot);

                Logger.Trace("Metrics collected (#{0})", _collectionCount);
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Error during metrics collection");
            }

            return snapshot;
        }

        /// <summary>
        /// Gets the current metrics snapshot.
        /// Thread-safe.
        /// </summary>
        public MetricsSnapshot GetCurrentSnapshot()
        {
            lock (_snapshotLock)
            {
                return _currentSnapshot?.Clone();
            }
        }

        /// <summary>
        /// Adds a snapshot to the history queue.
        /// </summary>
        private void AddToHistory(MetricsSnapshot snapshot)
        {
            _history.Enqueue(snapshot);

            // Trim if over capacity
            while (_history.Count > MaxHistorySize)
            {
                _history.TryDequeue(out _);
            }
        }

        /// <summary>
        /// Gets historical snapshots for trend analysis.
        /// </summary>
        /// <param name="maxCount">Maximum number of snapshots to return (newest first).</param>
        public MetricsSnapshot[] GetHistory(int maxCount = 100)
        {
            var historyArray = _history.ToArray();
            var count = Math.Min(maxCount, historyArray.Length);
            var result = new MetricsSnapshot[count];

            // Copy newest first
            for (int i = 0; i < count; i++)
            {
                result[i] = historyArray[historyArray.Length - 1 - i];
            }

            return result;
        }

        /// <summary>
        /// Logs a metrics summary to the metrics log.
        /// </summary>
        private void LogMetricsSummary()
        {
            var snapshot = GetCurrentSnapshot();
            if (snapshot == null)
                return;

            // Use the compact log format from MetricsSnapshot
            var logLine = snapshot.ToLogLine();
            MetricsLogger.Info(logLine);

            // Also log any alerts at warning level
            if (snapshot.HandleLeakSuspected)
            {
                MetricsLogger.Warn("ALERT: Handle leak suspected! Handles: {0}, Trend: +{1} over {2} samples",
                    snapshot.MBHandleCount, snapshot.HandleCountTrendTotal, snapshot.TrendSampleCount);
            }

            if (snapshot.MemoryLeakSuspected)
            {
                MetricsLogger.Warn("ALERT: Memory leak suspected! Private bytes: {0}MB, Trend: +{1:N0} bytes over {2} samples",
                    snapshot.MBPrivateBytesMB, snapshot.PrivateBytesTrendTotal, snapshot.TrendSampleCount);
            }

            if (snapshot.GCPressureHigh)
            {
                MetricsLogger.Warn("ALERT: High GC pressure! Gen2: {0}, Time in GC: {1:F1}%",
                    snapshot.GCGen2Collections, snapshot.GCTimePercent);
            }

            if (snapshot.LowMemoryWarning)
            {
                MetricsLogger.Warn("ALERT: Low system memory! Available: {0}MB / {1}MB ({2:F1}% used)",
                    snapshot.AvailableMemoryMB, snapshot.TotalMemoryMB, snapshot.MemoryPercent);
            }
        }

        /// <summary>
        /// Checks for leaks and fires events if detected.
        /// </summary>
        private void CheckForLeaks(MetricsSnapshot snapshot)
        {
            if (snapshot.HandleLeakSuspected || snapshot.MemoryLeakSuspected)
            {
                var args = new LeakDetectedEventArgs
                {
                    Snapshot = snapshot,
                    HandleLeakSuspected = snapshot.HandleLeakSuspected,
                    MemoryLeakSuspected = snapshot.MemoryLeakSuspected,
                    HandleCountDelta = snapshot.HandleCountTrendTotal,
                    PrivateBytesDelta = snapshot.PrivateBytesTrendTotal,
                    SampleCount = snapshot.TrendSampleCount
                };

                LeakDetected?.Invoke(this, args);
            }
        }

        /// <summary>
        /// Forces a garbage collection and returns diagnostics.
        /// Use sparingly!
        /// </summary>
        public string ForceGCAndReport()
        {
            var beforeSnapshot = GetCurrentSnapshot();
            var freedBytes = _clrMetrics.ForceGCAndMeasure();
            var afterSnapshot = CollectNow();

            return $@"Forced GC Report
================
Before:
  Managed Heap: {beforeSnapshot?.ManagedHeapMB}MB
  Private Bytes: {beforeSnapshot?.MBPrivateBytesMB}MB
  Working Set: {beforeSnapshot?.MBWorkingSetMB}MB

After:
  Managed Heap: {afterSnapshot?.ManagedHeapMB}MB
  Private Bytes: {afterSnapshot?.MBPrivateBytesMB}MB
  Working Set: {afterSnapshot?.MBWorkingSetMB}MB

Freed: {freedBytes / (1024 * 1024)}MB";
        }

        /// <summary>
        /// Gets a comprehensive diagnostic summary.
        /// </summary>
        public string GetDiagnosticSummary()
        {
            var snapshot = GetCurrentSnapshot();
            if (snapshot == null)
            {
                return "No metrics snapshot available.";
            }

            return $@"=== mb_clouseau Metrics Diagnostic Summary ===
Generated: {DateTime.Now:yyyy-MM-dd HH:mm:ss}
Collector Uptime: {Uptime}
Collection Count: {_collectionCount}

{snapshot.ToDetailedString()}

--- Process Diagnostics ---
{_processMetrics.GetDiagnosticSummary()}

--- CLR Diagnostics ---
{_clrMetrics.GetDiagnosticSummary()}

--- Audio Diagnostics ---
{_audioMetrics.GetDiagnosticSummary()}
";
        }

        /// <summary>
        /// Disposes all resources.
        /// </summary>
        public void Dispose()
        {
            if (_disposed)
                return;
            _disposed = true;

            try
            {
                Stop();

                _systemMetrics?.Dispose();
                _processMetrics?.Dispose();
                _clrMetrics?.Dispose();
                _audioMetrics?.Dispose();

                Logger.Debug("MetricsCollector disposed");
            }
            catch (Exception ex)
            {
                Logger.Warn(ex, "Error disposing MetricsCollector");
            }
        }
    }

    /// <summary>
    /// Event arguments for leak detection events.
    /// </summary>
    public class LeakDetectedEventArgs : EventArgs
    {
        /// <summary>
        /// The metrics snapshot when the leak was detected.
        /// </summary>
        public MetricsSnapshot Snapshot { get; set; }

        /// <summary>
        /// True if a handle leak is suspected.
        /// </summary>
        public bool HandleLeakSuspected { get; set; }

        /// <summary>
        /// True if a memory leak is suspected.
        /// </summary>
        public bool MemoryLeakSuspected { get; set; }

        /// <summary>
        /// Total handle count change over the trend window.
        /// </summary>
        public int HandleCountDelta { get; set; }

        /// <summary>
        /// Total private bytes change over the trend window.
        /// </summary>
        public long PrivateBytesDelta { get; set; }

        /// <summary>
        /// Number of samples in the trend window.
        /// </summary>
        public int SampleCount { get; set; }

        /// <summary>
        /// Formats a summary message for the leak detection.
        /// </summary>
        public override string ToString()
        {
            var messages = new System.Collections.Generic.List<string>();

            if (HandleLeakSuspected)
            {
                messages.Add($"Handle leak: +{HandleCountDelta} handles over {SampleCount} samples");
            }

            if (MemoryLeakSuspected)
            {
                var mb = PrivateBytesDelta / (1024.0 * 1024.0);
                messages.Add($"Memory leak: +{mb:F1}MB over {SampleCount} samples");
            }

            return string.Join("; ", messages);
        }
    }
}
