using EonaCat.LogStack.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace EonaCat.LogStack.Flows
{
    // This file is part of the EonaCat project(s) which is released under the Apache License.
    // See the LICENSE file or go to https://EonaCat.com/License for full license details.

    /// <summary>
    /// PerformanceAnomalyDetectorFlow detects anomalies in performance metrics and generates alerts.
    /// Uses statistical analysis to identify deviations from baseline performance.
    /// </summary>
    public sealed class PerformanceAnomalyDetectorFlow : FlowBase
    {
        private readonly int _windowSize;
        private readonly double _standardDeviationThreshold;
        private readonly IFlow _alertTarget;
        private readonly Dictionary<string, PerformanceMetricTracker> _trackers = new();
        private readonly object _trackersLock = new();

        /// <summary>
        /// Creates a new PerformanceAnomalyDetectorFlow
        /// </summary>
        /// <param name="alertTarget">Flow to send anomaly alerts to</param>
        /// <param name="windowSize">Size of the sliding window for statistical analysis (default 100)</param>
        /// <param name="standardDeviationThreshold">Alert threshold in standard deviations (default 2.0)</param>
        /// <param name="minimumLevel">Minimum log level to process</param>
        public PerformanceAnomalyDetectorFlow(
            IFlow? alertTarget = null,
            int windowSize = 100,
            double standardDeviationThreshold = 2.0,
            LogLevel minimumLevel = LogLevel.Information)
            : base("PerformanceAnomalyDetector", minimumLevel)
        {
            _windowSize = Math.Max(10, windowSize);
            _standardDeviationThreshold = Math.Max(1.0, standardDeviationThreshold);
            _alertTarget = alertTarget;
        }

        public override async Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken cancellationToken = default)
        {
            if (!IsEnabled || !IsLogLevelEnabled(logEvent))
                return WriteResult.Success;

            try
            {
                var metrics = ExtractPerformanceMetrics(logEvent);
                if (metrics.Count == 0)
                    return WriteResult.Success;

                lock (_trackersLock)
                {
                    foreach (var metric in metrics)
                    {
                        var key = metric.Key;
                        if (!_trackers.TryGetValue(key, out var tracker))
                        {
                            tracker = new PerformanceMetricTracker(key, _windowSize, _standardDeviationThreshold);
                            _trackers[key] = tracker;
                        }

                        var anomaly = tracker.AddMeasurement(metric.Value, logEvent);
                        if (anomaly != null && _alertTarget != null)
                        {
                            // Send anomaly alert
                            var alertEvent = new LogEvent
                            {
                                Timestamp = DateTime.UtcNow.Ticks,
                                Level = LogLevel.Warning,
                                Category = "PerformanceAnomaly",
                                Message = anomaly.Description.AsMemory(),
                                Properties = new Dictionary<string, object?>
                                {
                                    ["MetricName"] = anomaly.MetricName,
                                    ["CurrentValue"] = anomaly.CurrentValue,
                                    ["AverageValue"] = anomaly.AverageValue,
                                    ["Deviation"] = anomaly.DeviationMultiplier,
                                    ["IsSlowdown"] = anomaly.IsSlowdown
                                }
                            };

                            _ = _alertTarget.BlastAsync(alertEvent);
                        }
                    }
                }

                return await Task.FromResult(WriteResult.Success);
            }
            catch (Exception ex)
            {
                return WriteResult.Failed;
            }
        }

        public override async Task<WriteResult> BlastBatchAsync(ReadOnlyMemory<LogEvent> logEvents, CancellationToken cancellationToken = default)
        {
            foreach (var logEvent in logEvents.ToArray())
            {
                var result = await BlastAsync(logEvent, cancellationToken);
                if (result != WriteResult.Success)
                    return result;
            }
            return WriteResult.Success;
        }

        public override async Task FlushAsync(CancellationToken cancellationToken = default)
        {
            if (_alertTarget != null)
                await _alertTarget.FlushAsync(cancellationToken);
            await Task.CompletedTask;
        }

        /// <summary>
        /// Get anomaly detection statistics
        /// </summary>
        public AnomalyDetectionStatistics GetStatistics()
        {
            lock (_trackersLock)
            {
                var stats = new AnomalyDetectionStatistics
                {
                    MonitoredMetrics = _trackers.Count,
                    TotalMeasurements = _trackers.Values.Sum(t => t.MeasurementCount),
                    TotalAnomaliesDetected = _trackers.Values.Sum(t => t.AnomalyCount),
                    MetricStatistics = _trackers.ToDictionary(
                        kvp => kvp.Key,
                        kvp => new MetricStatistic
                        {
                            AverageValue = kvp.Value.Average,
                            StandardDeviation = kvp.Value.StandardDeviation,
                            MinValue = kvp.Value.MinValue,
                            MaxValue = kvp.Value.MaxValue,
                            AnomalyCount = kvp.Value.AnomalyCount
                        })
                };

                return stats;
            }
        }

        /// <summary>
        /// Get recent anomalies
        /// </summary>
        public IEnumerable<PerformanceAnomaly> GetRecentAnomalies(int count = 10)
        {
            lock (_trackersLock)
            {
                return _trackers.Values
                    .SelectMany(t => t.RecentAnomalies)
                    .OrderByDescending(a => a.DetectedAt)
                    .Take(count)
                    .ToList();
            }
        }

        /// <summary>
        /// Get anomalies for a specific metric
        /// </summary>
        public IEnumerable<PerformanceAnomaly> GetAnomaliesForMetric(string metricName)
        {
            lock (_trackersLock)
            {
                if (_trackers.TryGetValue(metricName, out var tracker))
                    return tracker.RecentAnomalies.ToList();
                return new List<PerformanceAnomaly>();
            }
        }

        /// <summary>
        /// Reset anomaly tracking for a metric
        /// </summary>
        public void ResetMetric(string metricName)
        {
            lock (_trackersLock)
            {
                _trackers.Remove(metricName);
            }
        }

        /// <summary>
        /// Clear all tracking data
        /// </summary>
        public void Clear()
        {
            lock (_trackersLock)
            {
                _trackers.Clear();
            }
        }

        // Private helpers

        private Dictionary<string, double> ExtractPerformanceMetrics(LogEvent logEvent)
        {
            var metrics = new Dictionary<string, double>();

            if (logEvent.Properties == null)
                return metrics;

            // Look for common performance metric property names
            var metricNames = new[] { "duration", "latency", "elapsed", "time_ms", "response_time", "memory", "cpu" };

            foreach (var propKey in logEvent.Properties.Keys)
            {
                var keyStr = propKey.ToString();
                if (metricNames.Any(mn => keyStr.Contains(mn, StringComparison.OrdinalIgnoreCase)))
                {
                    if (logEvent.Properties[propKey] is double dVal)
                    {
                        metrics[keyStr] = dVal;
                    }
                    else if (logEvent.Properties[propKey] is int iVal)
                    {
                        metrics[keyStr] = iVal;
                    }
                    else if (logEvent.Properties[propKey] is long lVal)
                    {
                        metrics[keyStr] = lVal;
                    }
                    else if (double.TryParse(logEvent.Properties[propKey]?.ToString(), out var parsed))
                    {
                        metrics[keyStr] = parsed;
                    }
                }
            }

            return metrics;
        }

        public override async ValueTask DisposeAsync()
        {
            Clear();
            if (_alertTarget != null)
                await _alertTarget.DisposeAsync();
            await base.DisposeAsync();
        }
    }

    /// <summary>
    /// Tracks performance metrics and detects anomalies
    /// </summary>
    public class PerformanceMetricTracker
    {
        private readonly string _metricName;
        private readonly int _windowSize;
        private readonly double _threshold;
        private readonly List<double> _measurements = new();
        private readonly List<PerformanceAnomaly> _recentAnomalies = new();
        private double _sum = 0;

        public string MetricName => _metricName;
        public int MeasurementCount => _measurements.Count;
        public int AnomalyCount { get; private set; } = 0;
        public IReadOnlyList<PerformanceAnomaly> RecentAnomalies => _recentAnomalies.AsReadOnly();

        public double Average => _measurements.Count == 0 ? 0 : _sum / _measurements.Count;
        public double StandardDeviation => CalculateStandardDeviation();
        public double MinValue => _measurements.Count == 0 ? 0 : _measurements.Min();
        public double MaxValue => _measurements.Count == 0 ? 0 : _measurements.Max();

        public PerformanceMetricTracker(string metricName, int windowSize, double threshold)
        {
            _metricName = metricName;
            _windowSize = windowSize;
            _threshold = threshold;
        }

        public PerformanceAnomaly? AddMeasurement(double value, LogEvent sourceEvent)
        {
            // Add measurement
            _measurements.Add(value);
            _sum += value;

            // Keep only recent measurements
            if (_measurements.Count > _windowSize)
            {
                _sum -= _measurements[0];
                _measurements.RemoveAt(0);
            }

            // Check for anomaly
            if (_measurements.Count >= 10) // Need enough data for statistical analysis
            {
                var avg = Average;
                var stdDev = StandardDeviation;

                if (stdDev > 0)
                {
                    var zScore = Math.Abs((value - avg) / stdDev);
                    if (zScore > _threshold)
                    {
                        var isSlowdown = value > avg;
                        var anomaly = new PerformanceAnomaly
                        {
                            MetricName = _metricName,
                            CurrentValue = value,
                            AverageValue = avg,
                            StandardDeviation = stdDev,
                            DeviationMultiplier = zScore,
                            IsSlowdown = isSlowdown,
                            DetectedAt = DateTime.UtcNow,
                            Description = $"Performance anomaly detected in '{_metricName}': " +
                                        $"value {value:F2} is {(isSlowdown ? "higher" : "lower")} " +
                                        $"than average {avg:F2} by {zScore:F2} standard deviations"
                        };

                        _recentAnomalies.Add(anomaly);
                        if (_recentAnomalies.Count > 100)
                            _recentAnomalies.RemoveAt(0);

                        AnomalyCount++;
                        return anomaly;
                    }
                }
            }

            return null;
        }

        private double CalculateStandardDeviation()
        {
            if (_measurements.Count < 2)
                return 0;

            var avg = Average;
            var sumSquaredDiff = _measurements.Sum(x => Math.Pow(x - avg, 2));
            return Math.Sqrt(sumSquaredDiff / _measurements.Count);
        }
    }

    /// <summary>
    /// Represents a detected performance anomaly
    /// </summary>
    public class PerformanceAnomaly
    {
        public string MetricName { get; set; }
        public double CurrentValue { get; set; }
        public double AverageValue { get; set; }
        public double StandardDeviation { get; set; }
        public double DeviationMultiplier { get; set; }
        public bool IsSlowdown { get; set; }
        public DateTime DetectedAt { get; set; }
        public string Description { get; set; }

        public override string ToString()
        {
            return $"Anomaly[{MetricName}]: {CurrentValue:F2} (avg: {AverageValue:F2}, " +
                   $"deviation: {DeviationMultiplier:F2}σ) - {(IsSlowdown ? "SLOWDOWN" : "SPEEDUP")}";
        }
    }

    /// <summary>
    /// Statistics about anomaly detection
    /// </summary>
    public class AnomalyDetectionStatistics
    {
        public int MonitoredMetrics { get; set; }
        public int TotalMeasurements { get; set; }
        public int TotalAnomaliesDetected { get; set; }
        public Dictionary<string, MetricStatistic> MetricStatistics { get; set; } = new();

        public override string ToString()
        {
            return $"AnomalyStats: Metrics={MonitoredMetrics}, Measurements={TotalMeasurements}, " +
                   $"Anomalies={TotalAnomaliesDetected}";
        }
    }

    /// <summary>
    /// Statistics for a single metric
    /// </summary>
    public class MetricStatistic
    {
        public double AverageValue { get; set; }
        public double StandardDeviation { get; set; }
        public double MinValue { get; set; }
        public double MaxValue { get; set; }
        public int AnomalyCount { get; set; }
    }
}
