using EonaCat.LogStack.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

// 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.

namespace EonaCat.LogStack.Anomalies;

/// <summary>
/// Real-time anomaly detector that identifies unusual logging patterns and escalates them.
/// Superior to simple threshold-based alerting as it learns normal behavior over time.
/// </summary>
public class AnomalyDetector
{
    private readonly ConcurrentDictionary<string, CategoryStatistics> _categoryStats 
        = new ConcurrentDictionary<string, CategoryStatistics>();

    private readonly ConcurrentDictionary<LogLevel, LevelStatistics> _levelStats 
        = new ConcurrentDictionary<LogLevel, LevelStatistics>();

    private readonly double _standardDeviationThreshold;
    private readonly int _windowSizeSeconds;
    private readonly object _statsLock = new object();
    private long _totalEventsProcessed;

    private readonly List<AnomalyAlert> _recentAlerts = new List<AnomalyAlert>();
    private readonly Stopwatch _uptime = Stopwatch.StartNew();

    public event EventHandler<AnomalyAlert> AnomalyDetected;

    public AnomalyDetector(
        double standardDeviationThreshold = 3.0,
        int windowSizeSeconds = 300)
    {
        _standardDeviationThreshold = standardDeviationThreshold;
        _windowSizeSeconds = windowSizeSeconds;
    }

    /// <summary>
    /// Analyzes a log event for anomalies.
    /// </summary>
    public AnomalyAlert AnalyzeEvent(LogEvent logEvent)
    {

        _totalEventsProcessed++;

        // Track category statistics
        var categoryStats = _categoryStats.GetOrAdd(logEvent.Category ?? "Unknown", 
            _ => new CategoryStatistics());
        categoryStats.RecordEvent(logEvent);

        // Track level statistics
        var levelStats = _levelStats.GetOrAdd(logEvent.Level, 
            _ => new LevelStatistics());
        levelStats.RecordEvent();

        // Detect anomalies
        var anomalies = new List<string>();

        // Check for unusual error spike
        if (logEvent.Level == LogLevel.Error || logEvent.Level == LogLevel.Critical)
        {
            var errorRate = levelStats.GetErrorRatePerSecond();
            if (errorRate > 50) // More than 50 errors per second
            {
                anomalies.Add($"High error rate detected: {errorRate:F2} errors/sec");
            }
        }

        // Check for category anomalies
        if (categoryStats.IsAnomalous(_standardDeviationThreshold))
        {
            anomalies.Add($"Category '{logEvent.Category}' showing unusual activity");
        }

        // Check for exception frequency spike
        if (logEvent.Exception != null)
        {
            var exceptionKey = logEvent.Exception.GetType().Name;
            if (categoryStats.RecordException(exceptionKey) > 5)
            {
                anomalies.Add($"Exception '{exceptionKey}' spike detected");
            }
        }

        // Check for message repetition
        if (logEvent.Message.Length > 0)
        {
            if (categoryStats.RecordMessageHash(logEvent.Message.ToString().GetHashCode()) > 10)
            {
                anomalies.Add("Repeated message pattern detected (possible infinite loop)");
            }
        }

        if (anomalies.Any())
        {
            var alert = new AnomalyAlert
            {
                Timestamp = DateTime.UtcNow,
                Category = logEvent.Category,
                Level = logEvent.Level,
                LogEvent = logEvent,
                Anomalies = anomalies,
                AnomalySeverity = DetermineAnomalySeverity(anomalies),
                SystemMetrics = CaptureSystemMetrics()
            };

            lock (_statsLock)
            {
                _recentAlerts.Add(alert);
                if (_recentAlerts.Count > 1000)
                    _recentAlerts.RemoveAt(0); // Keep last 1000 alerts
            }

            AnomalyDetected?.Invoke(this, alert);
            return alert;
        }

        return null;
    }

    /// <summary>
    /// Gets statistics summary for analysis.
    /// </summary>
    public AnomalyDetectorStats GetStats()
    {
        lock (_statsLock)
        {
            return new AnomalyDetectorStats
            {
                TotalEventsProcessed = _totalEventsProcessed,
                UniqueCategories = _categoryStats.Count,
                RecentAnomalies = _recentAlerts.Count,
                AnomalyRate = _recentAlerts.Count / Math.Max(1, _uptime.Elapsed.TotalMinutes),
                TopCategories = _categoryStats
                    .OrderByDescending(x => x.Value.EventCount)
                    .Take(5)
                    .Select(x => new CategorySummary 
                    { 
                        Category = x.Key, 
                        EventCount = x.Value.EventCount,
                        IsAnomalous = x.Value.IsAnomalous(_standardDeviationThreshold)
                    })
                    .ToList()
            };
        }
    }

    /// <summary>
    /// Gets recent detected anomalies.
    /// </summary>
    public List<AnomalyAlert> GetRecentAnomalies(int count = 10)
    {
        lock (_statsLock)
        {
            return _recentAlerts.Skip(Math.Max(0, _recentAlerts.Count - count))
                .OrderBy(a => a.Timestamp)
                .ToList();
        }
    }

    /// <summary>
    /// Clears all statistics (useful for resetting baseline after deployment).
    /// </summary>
    public void ResetStatistics()
    {
        _categoryStats.Clear();
        _levelStats.Clear();
        lock (_statsLock)
        {
            _recentAlerts.Clear();
        }
        _totalEventsProcessed = 0;
    }

    private AnomalySeverity DetermineAnomalySeverity(List<string> anomalies)
    {
        if (anomalies.Any(a => a.Contains("error rate")))
            return AnomalySeverity.Critical;

        if (anomalies.Any(a => a.Contains("Exception") || a.Contains("infinite loop")))
            return AnomalySeverity.High;

        return AnomalySeverity.Medium;
    }

    private SystemMetrics CaptureSystemMetrics()
    {
        var process = Process.GetCurrentProcess();
        return new SystemMetrics
        {
            ProcessMemoryMb = process.WorkingSet64 / (1024 * 1024),
            ProcessorCount = Environment.ProcessorCount,
            Uptime = _uptime.Elapsed,
            ThreadCount = process.Threads.Count,
            TotalEventsProcessed = _totalEventsProcessed
        };
    }
}

/// <summary>
/// Tracks statistics for a log category.
/// </summary>
internal class CategoryStatistics
{
    private readonly Queue<DateTime> _recentEventTimes = new Queue<DateTime>();
    private readonly ConcurrentDictionary<int, int> _messageHashCounts = new ConcurrentDictionary<int, int>();
    private readonly ConcurrentDictionary<string, int> _exceptionCounts = new ConcurrentDictionary<string, int>();

    public long EventCount { get; private set; }
    private long _errorCount;
    private readonly object _lock = new object();

    public void RecordEvent(LogEvent logEvent)
    {
        lock (_lock)
        {
            EventCount++;
            _recentEventTimes.Enqueue(DateTime.UtcNow);
            if (_recentEventTimes.Count > 1000)
                _recentEventTimes.Dequeue();

            if (logEvent.Level == LogLevel.Error || logEvent.Level == LogLevel.Critical)
                _errorCount++;
        }
    }

    public int RecordException(string exceptionType)
    {
        return _exceptionCounts.AddOrUpdate(exceptionType, 1, (k, v) => v + 1);
    }

    public int RecordMessageHash(int messageHash)
    {
        return _messageHashCounts.AddOrUpdate(messageHash, 1, (k, v) => v + 1);
    }

    public bool IsAnomalous(double stdDevThreshold)
    {
        lock (_lock)
        {
            if (_recentEventTimes.Count < 10)
                return false;

            var now = DateTime.UtcNow;
            var times = _recentEventTimes.ToList();
            var recentIntervals = new List<double>();

            for (int i = 1; i < times.Count; i++)
            {
                var interval = (now - times[i]).TotalSeconds;
                if (interval > 0)
                    recentIntervals.Add(interval);
            }

            if (recentIntervals.Count < 2)
                return false;

            var mean = recentIntervals.Average();
            var variance = recentIntervals.Average(x => Math.Pow(x - mean, 2));
            var stdDev = Math.Sqrt(variance);

            // Event times are anomalous if they're too frequent (low stdDev) or erratic (high stdDev)
            return stdDev < 0.1 || stdDev > stdDevThreshold;
        }
    }
}

/// <summary>
/// Tracks statistics for a log level.
/// </summary>
internal class LevelStatistics
{
    private readonly Queue<DateTime> _eventTimes = new Queue<DateTime>();
    private readonly object _lock = new object();
    private long _errorCount;

    public void RecordEvent()
    {
        lock (_lock)
        {
            _errorCount++;
            _eventTimes.Enqueue(DateTime.UtcNow);
            if (_eventTimes.Count > 10000)
                _eventTimes.Dequeue();
        }
    }

    public double GetErrorRatePerSecond()
    {
        lock (_lock)
        {
            if (_eventTimes.Count < 2)
                return 0;

            var now = DateTime.UtcNow;
            var span = (now - _eventTimes.Peek()).TotalSeconds;

            if (span < 1)
                return _eventTimes.Count;

            return _eventTimes.Count / span;
        }
    }
}

/// <summary>
/// An alert raised when an anomaly is detected.
/// </summary>
public class AnomalyAlert
{
    public DateTime Timestamp { get; set; }
    public string Category { get; set; }
    public LogLevel Level { get; set; }
    public LogEvent LogEvent { get; set; }
    public List<string> Anomalies { get; set; } = new List<string>();
    public AnomalySeverity AnomalySeverity { get; set; }
    public SystemMetrics SystemMetrics { get; set; }
}

/// <summary>
/// Severity of detected anomaly.
/// </summary>
public enum AnomalySeverity
{
    Low,
    Medium,
    High,
    Critical
}

/// <summary>
/// System metrics captured when anomaly detected.
/// </summary>
public class SystemMetrics
{
    public long ProcessMemoryMb { get; set; }
    public int ProcessorCount { get; set; }
    public TimeSpan Uptime { get; set; }
    public int ThreadCount { get; set; }
    public long TotalEventsProcessed { get; set; }
}

/// <summary>
/// Statistics about anomaly detector.
/// </summary>
public class AnomalyDetectorStats
{
    public long TotalEventsProcessed { get; set; }
    public int UniqueCategories { get; set; }
    public int RecentAnomalies { get; set; }
    public double AnomalyRate { get; set; }
    public List<CategorySummary> TopCategories { get; set; } = new List<CategorySummary>();
}

/// <summary>
/// Summary of a category's status.
/// </summary>
public class CategorySummary
{
    public string Category { get; set; }
    public long EventCount { get; set; }
    public bool IsAnomalous { get; set; }
}
