using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace EonaCat.LogStack.Analytics;

// 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>
/// Tracks logging metrics and analytics with comprehensive per-level and per-category metrics
/// </summary>
public sealed class LoggingMetrics
{
    private readonly object _lock = new object();
    private long _totalEvents;
    private long _totalBytes;
    private long _totalExceptions;
    private long _droppedCount;
    private long _droppedExceptions;
    private readonly Dictionary<string, long> _levelCounts = new();
    private readonly Dictionary<string, long> _loggerCounts = new();
    private readonly Dictionary<string, long> _categoryCounts = new();
    private readonly List<long> _latencies = new();
    private long _minLatencyMs = long.MaxValue;
    private long _maxLatencyMs;
    private readonly Stopwatch _uptime = Stopwatch.StartNew();
    private double _p50LatencyMs;
    private double _p95LatencyMs;
    private double _p99LatencyMs;

    /// <summary>
    /// Records a log event with comprehensive metrics
    /// </summary>
    public void RecordEvent(object logEvent, long latencyMs, long bytes)
    {
        lock (_lock)
        {
            _totalEvents++;
            _totalBytes += bytes;
            _latencies.Add(latencyMs);

            if (latencyMs < _minLatencyMs)
            {
                _minLatencyMs = latencyMs;
            }

            if (latencyMs > _maxLatencyMs)
            {
                _maxLatencyMs = latencyMs;
            }

            // Keep only last 1000 latencies to avoid memory bloat
            if (_latencies.Count > 1000)
            {
                _latencies.RemoveAt(0);
            }
        }
    }

    /// <summary>
    /// Records a log event with level information
    /// </summary>
    public void RecordEventWithLevel(string level, long latencyMs, long bytes)
    {
        lock (_lock)
        {
            _totalEvents++;
            _totalBytes += bytes;
            _latencies.Add(latencyMs);

            if (latencyMs < _minLatencyMs)
            {
                _minLatencyMs = latencyMs;
            }

            if (latencyMs > _maxLatencyMs)
            {
                _maxLatencyMs = latencyMs;
            }

            // Track per-level counts
            if (!_levelCounts.ContainsKey(level))
            {
                _levelCounts[level] = 0;
            }

            _levelCounts[level]++;

            if (_latencies.Count > 1000)
            {
                _latencies.RemoveAt(0);
            }
        }
    }

    /// <summary>
    /// Records an exception
    /// </summary>
    public void RecordException(long latencyMs = 0)
    {
        Interlocked.Increment(ref _totalExceptions);
    }

    /// <summary>
    /// Records dropped events
    /// </summary>
    public void RecordDropped(long count = 1)
    {
        Interlocked.Add(ref _droppedCount, count);
    }

    /// <summary>
    /// Records dropped exceptions
    /// </summary>
    public void RecordDroppedException()
    {
        Interlocked.Increment(ref _droppedExceptions);
    }

    /// <summary>
    /// Records logger usage
    /// </summary>
    public void RecordLoggerUsage(string loggerName)
    {
        lock (_lock)
        {
            if (!_loggerCounts.ContainsKey(loggerName))
            {
                _loggerCounts[loggerName] = 0;
            }

            _loggerCounts[loggerName]++;
        }
    }

    /// <summary>
    /// Records category usage
    /// </summary>
    public void RecordCategoryUsage(string category)
    {
        lock (_lock)
        {
            if (!_categoryCounts.ContainsKey(category))
            {
                _categoryCounts[category] = 0;
            }

            _categoryCounts[category]++;
        }
    }

    /// <summary>
    /// Gets metrics snapshot
    /// </summary>
    public MetricsSnapshot GetSnapshot()
    {
        lock (_lock)
        {
            double avgLatency = _latencies.Count > 0 ? _latencies.Average() : 0;
            double median = CalculateMedian(_latencies);
            CalculatePercentiles(_latencies, out var p50, out var p95, out var p99);
            _p50LatencyMs = p50;
            _p95LatencyMs = p95;
            _p99LatencyMs = p99;

            var topLoggers = _loggerCounts
                .OrderByDescending(kvp => kvp.Value)
                .Take(10)
                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            var topCategories = _categoryCounts
                .OrderByDescending(kvp => kvp.Value)
                .Take(10)
                .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            return new MetricsSnapshot
            {
                TotalEvents = _totalEvents,
                TotalBytes = _totalBytes,
                TotalExceptions = _totalExceptions,
                DroppedCount = _droppedCount,
                DroppedExceptions = _droppedExceptions,
                LevelCounts = new Dictionary<string, long>(_levelCounts),
                LoggerCounts = topLoggers,
                CategoryCounts = topCategories,
                MinLatencyMs = _minLatencyMs == long.MaxValue ? 0 : _minLatencyMs,
                MaxLatencyMs = _maxLatencyMs,
                AverageLatencyMs = avgLatency,
                MedianLatencyMs = median,
                P50LatencyMs = p50,
                P95LatencyMs = p95,
                P99LatencyMs = p99,
                UptimeMs = _uptime.ElapsedMilliseconds,
                EventsPerSecond = _uptime.ElapsedMilliseconds > 0
                    ? _totalEvents * 1000.0 / _uptime.ElapsedMilliseconds
                    : 0,
                BytesPerSecond = _uptime.ElapsedMilliseconds > 0
                    ? _totalBytes * 1000.0 / _uptime.ElapsedMilliseconds
                    : 0,
                AverageEventSizeBytes = _totalEvents > 0 ? _totalBytes / (double)_totalEvents : 0
            };
        }
    }


    /// <summary>
    /// Resets all metrics
    /// </summary>
    public void Reset()
    {
        lock (_lock)
        {
            _totalEvents = 0;
            _totalBytes = 0;
            _totalExceptions = 0;
            _droppedCount = 0;
            _droppedExceptions = 0;
            _latencies.Clear();
            _minLatencyMs = long.MaxValue;
            _maxLatencyMs = 0;
            _levelCounts.Clear();
            _loggerCounts.Clear();
            _categoryCounts.Clear();
            _uptime.Restart();
        }
    }

    private double CalculateMedian(List<long> values)
    {
        if (values.Count == 0)
        {
            return 0;
        }

        var sorted = values.OrderBy(v => v).ToList();
        int mid = sorted.Count / 2;

        return sorted.Count % 2 == 0
            ? (sorted[mid - 1] + sorted[mid]) / 2.0
            : sorted[mid];
    }

    private void CalculatePercentiles(List<long> values, out double p50, out double p95, out double p99)
    {
        if (values.Count == 0)
        {
            p50 = p95 = p99 = 0;
            return;
        }

        var sorted = values.OrderBy(v => v).ToList();
        p50 = GetPercentile(sorted, 50);
        p95 = GetPercentile(sorted, 95);
        p99 = GetPercentile(sorted, 99);
    }

    private double GetPercentile(List<long> sortedValues, int percentile)
    {
        if (sortedValues.Count == 0)
        {
            return 0;
        }

        int index = (int)((percentile / 100.0) * (sortedValues.Count - 1));
        index = Math.Max(0, Math.Min(index, sortedValues.Count - 1));
        return sortedValues[index];
    }
}

/// <summary>
/// Snapshot of logging metrics at a point in time
/// </summary>
public sealed class MetricsSnapshot
{
    public long TotalEvents { get; set; }
    public long TotalBytes { get; set; }
    public long TotalExceptions { get; set; }
    public long DroppedCount { get; set; }
    public long DroppedExceptions { get; set; }
    public Dictionary<string, long> LevelCounts { get; set; } = new();
    public Dictionary<string, long> LoggerCounts { get; set; } = new();
    public Dictionary<string, long> CategoryCounts { get; set; } = new();
    public long MinLatencyMs { get; set; }
    public long MaxLatencyMs { get; set; }
    public double AverageLatencyMs { get; set; }
    public double MedianLatencyMs { get; set; }
    public double P50LatencyMs { get; set; }
    public double P95LatencyMs { get; set; }
    public double P99LatencyMs { get; set; }
    public long UptimeMs { get; set; }
    public double EventsPerSecond { get; set; }
    public double BytesPerSecond { get; set; }
    public double AverageEventSizeBytes { get; set; }

    /// <summary>
    /// Returns formatted report
    /// </summary>
    public override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine("===== LOGGING METRICS =====");
        sb.AppendLine($"Total Events: {TotalEvents:N0}");
        sb.AppendLine($"Total Bytes: {TotalBytes:N0}");
        sb.AppendLine($"Average Event Size: {AverageEventSizeBytes:F2} bytes");
        sb.AppendLine($"Events/Second: {EventsPerSecond:F2}");
        sb.AppendLine($"Bytes/Second: {BytesPerSecond:F2}");
        sb.AppendLine($"Total Exceptions: {TotalExceptions:N0}");
        sb.AppendLine($"Dropped Events: {DroppedCount:N0}");
        sb.AppendLine($"Dropped Exceptions: {DroppedExceptions:N0}");
        sb.AppendLine();

        if (LevelCounts.Count > 0)
        {
            sb.AppendLine("Events by Level:");
            foreach (var level in new[] { "Trace", "Debug", "Information", "Warning", "Error", "Critical" })
            {
                if (LevelCounts.TryGetValue(level, out var count))
                {
                    sb.AppendLine($"  {level}: {count:N0}");
                }
            }
            sb.AppendLine();
        }

        sb.AppendLine("Latency (ms):");
        sb.AppendLine($"  Min: {MinLatencyMs}");
        sb.AppendLine($"  P50: {P50LatencyMs:F3}");
        sb.AppendLine($"  P95: {P95LatencyMs:F3}");
        sb.AppendLine($"  P99: {P99LatencyMs:F3}");
        sb.AppendLine($"  Avg: {AverageLatencyMs:F3}");
        sb.AppendLine($"  Max: {MaxLatencyMs}");
        sb.AppendLine();

        if (CategoryCounts.Count > 0)
        {
            sb.AppendLine("Top Categories:");
            foreach (var kvp in CategoryCounts.Take(5))
            {
                sb.AppendLine($"  {kvp.Key}: {kvp.Value:N0}");
            }
            sb.AppendLine();
        }

        sb.AppendLine($"Uptime: {TimeSpan.FromMilliseconds(UptimeMs):hh\\:mm\\:ss}");
        sb.AppendLine("===========================");
        return sb.ToString();
    }
}

/// <summary>
/// Logger introspection API stub for inspecting logger configuration
/// </summary>
public sealed class LoggerIntrospection
{
    private readonly List<string> _flows = new();
    private readonly List<string> _boosters = new();

    public LoggerIntrospection(object logger)
    {
        // Logger integration to be provided when IFlowLogger is available
    }

    /// <summary>
    /// Adds a flow for tracking
    /// </summary>
    public void RegisterFlow(string name)
    {
        _flows.Add(name);
    }

    /// <summary>
    /// Adds a booster for tracking
    /// </summary>
    public void RegisterBooster(string name)
    {
        _boosters.Add(name);
    }

    /// <summary>
    /// Gets configured flows
    /// </summary>
    public IReadOnlyList<string> GetFlows() => _flows.AsReadOnly();

    /// <summary>
    /// Gets configured boosters
    /// </summary>
    public IReadOnlyList<string> GetBoosters() => _boosters.AsReadOnly();

    /// <summary>
    /// Generates a report of logger configuration
    /// </summary>
    public string GenerateReport()
    {
        var sb = new StringBuilder();
        sb.AppendLine("===== LOGGER CONFIGURATION =====");
        sb.AppendLine($"Total Flows: {_flows.Count}");
        foreach (var flow in _flows)
        {
            sb.AppendLine($"  - {flow}");
        }

        sb.AppendLine();
        sb.AppendLine($"Total Boosters: {_boosters.Count}");
        foreach (var booster in _boosters)
        {
            sb.AppendLine($"  - {booster}");
        }

        sb.AppendLine("================================");
        return sb.ToString();
    }
}

/// <summary>
/// Performance analyzer for logging
/// </summary>
public sealed class LoggingPerformanceAnalyzer
{
    private readonly Dictionary<string, OperationMetrics> _operations = new();
    private readonly object _lock = new();

    /// <summary>
    /// Measures execution time of a function
    /// </summary>
    public T MeasureOperation<T>(string operationName, Func<T> operation)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            return operation();
        }
        finally
        {
            sw.Stop();
            RecordOperation(operationName, sw.ElapsedMilliseconds);
        }
    }

    /// <summary>
    /// Measures execution time of an action
    /// </summary>
    public void MeasureOperation(string operationName, Action operation)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            operation();
        }
        finally
        {
            sw.Stop();
            RecordOperation(operationName, sw.ElapsedMilliseconds);
        }
    }

    private void RecordOperation(string name, long ms)
    {
        lock (_lock)
        {
            if (!_operations.TryGetValue(name, out var metrics))
            {
                metrics = new OperationMetrics { OperationName = name };
                _operations[name] = metrics;
            }

            metrics.Count++;
            metrics.TotalTimeMs += ms;
            metrics.MinTimeMs = Math.Min(metrics.MinTimeMs, ms);
            metrics.MaxTimeMs = Math.Max(metrics.MaxTimeMs, ms);
        }
    }

    /// <summary>
    /// Gets all recorded operations
    /// </summary>
    public IReadOnlyCollection<OperationMetrics> GetOperations()
    {
        lock (_lock)
        {
            return _operations.Values.ToList().AsReadOnly();
        }
    }

    /// <summary>
    /// Generates performance report
    /// </summary>
    public string GenerateReport()
    {
        lock (_lock)
        {
            var sb = new StringBuilder();
            sb.AppendLine("===== PERFORMANCE ANALYSIS =====");

            foreach (var op in _operations.Values.OrderByDescending(o => o.TotalTimeMs))
            {
                double avgTime = op.TotalTimeMs / (double)op.Count;
                sb.AppendLine($"{op.OperationName}:");
                sb.AppendLine($"  Count: {op.Count}");
                sb.AppendLine($"  Total: {op.TotalTimeMs}ms");
                sb.AppendLine($"  Average: {avgTime:F3}ms");
                sb.AppendLine($"  Min: {op.MinTimeMs}ms");
                sb.AppendLine($"  Max: {op.MaxTimeMs}ms");
            }

            sb.AppendLine("================================");
            return sb.ToString();
        }
    }

    public void Reset()
    {
        lock (_lock)
        {
            _operations.Clear();
        }
    }
}

/// <summary>
/// Operation metrics
/// </summary>
public sealed class OperationMetrics
{
    public string OperationName { get; set; } = "";
    public long Count { get; set; }
    public long TotalTimeMs { get; set; }
    public long MinTimeMs { get; set; } = long.MaxValue;
    public long MaxTimeMs { get; set; }

    public double AverageTimeMs => TotalTimeMs / (double)(Count > 0 ? Count : 1);
}
