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 statistics for an individual logging flow
/// </summary>
public sealed class PerFlowStatistics
{
    private readonly object _lock = new object();
    private long _eventsProcessed;
    private long _eventsFailed;
    private long _bytesWritten;
    private long _eventsDropped;
    private readonly List<long> _latencies = new();
    private long _minLatencyMs = long.MaxValue;
    private long _maxLatencyMs;
    private readonly Stopwatch _uptime = Stopwatch.StartNew();

    public string FlowName { get; set; }
    public string FlowType { get; set; }

    public PerFlowStatistics(string flowName, string flowType)
    {
        FlowName = flowName ?? "Unknown";
        FlowType = flowType ?? "Unknown";
    }

    /// <summary>
    /// Records a successfully processed event
    /// </summary>
    public void RecordSuccess(long latencyMs, long bytes)
    {
        lock (_lock)
        {
            _eventsProcessed++;
            _bytesWritten += bytes;
            _latencies.Add(latencyMs);

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

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

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

    /// <summary>
    /// Records a failed event
    /// </summary>
    public void RecordFailure(long latencyMs = 0)
    {
        Interlocked.Increment(ref _eventsFailed);
        if (latencyMs > 0)
        {
            lock (_lock)
            {
                _latencies.Add(latencyMs);
                if (latencyMs < _minLatencyMs)
                {
                    _minLatencyMs = latencyMs;
                }

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

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

    /// <summary>
    /// Gets a snapshot of current statistics
    /// </summary>
    public FlowStatisticsSnapshot GetSnapshot()
    {
        lock (_lock)
        {
            double avgLatency = _latencies.Count > 0 ? _latencies.Average() : 0;
            double p95 = GetPercentile(_latencies, 95);
            double p99 = GetPercentile(_latencies, 99);

            var totalAttempts = _eventsProcessed + _eventsFailed;
            var successRate = totalAttempts > 0 ? (_eventsProcessed * 100.0) / totalAttempts : 100;

            return new FlowStatisticsSnapshot
            {
                FlowName = FlowName,
                FlowType = FlowType,
                EventsProcessed = _eventsProcessed,
                EventsFailed = _eventsFailed,
                EventsDropped = _eventsDropped,
                BytesWritten = _bytesWritten,
                SuccessRate = successRate,
                MinLatencyMs = _minLatencyMs == long.MaxValue ? 0 : _minLatencyMs,
                MaxLatencyMs = _maxLatencyMs,
                AverageLatencyMs = avgLatency,
                P95LatencyMs = p95,
                P99LatencyMs = p99,
                UptimeMs = _uptime.ElapsedMilliseconds,
                EventsPerSecond = _uptime.ElapsedMilliseconds > 0
                    ? _eventsProcessed * 1000.0 / _uptime.ElapsedMilliseconds
                    : 0,
                BytesPerSecond = _uptime.ElapsedMilliseconds > 0
                    ? _bytesWritten * 1000.0 / _uptime.ElapsedMilliseconds
                    : 0
            };
        }
    }

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

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

    /// <summary>
    /// Resets all statistics
    /// </summary>
    public void Reset()
    {
        lock (_lock)
        {
            _eventsProcessed = 0;
            _eventsFailed = 0;
            _eventsDropped = 0;
            _bytesWritten = 0;
            _latencies.Clear();
            _minLatencyMs = long.MaxValue;
            _maxLatencyMs = 0;
            _uptime.Restart();
        }
    }
}

/// <summary>
/// Snapshot of flow statistics at a point in time
/// </summary>
public sealed class FlowStatisticsSnapshot
{
    public string FlowName { get; set; }
    public string FlowType { get; set; }
    public long EventsProcessed { get; set; }
    public long EventsFailed { get; set; }
    public long EventsDropped { get; set; }
    public long BytesWritten { get; set; }
    public double SuccessRate { get; set; }
    public long MinLatencyMs { get; set; }
    public long MaxLatencyMs { get; set; }
    public double AverageLatencyMs { 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 override string ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine($"===== FLOW: {FlowName} ({FlowType}) =====");
        sb.AppendLine($"Events Processed: {EventsProcessed:N0}");
        sb.AppendLine($"Events Failed: {EventsFailed:N0}");
        sb.AppendLine($"Events Dropped: {EventsDropped:N0}");
        sb.AppendLine($"Success Rate: {SuccessRate:F2}%");
        sb.AppendLine($"Bytes Written: {BytesWritten:N0}");
        sb.AppendLine($"Events/Second: {EventsPerSecond:F2}");
        sb.AppendLine($"Bytes/Second: {BytesPerSecond:F2}");
        sb.AppendLine();
        sb.AppendLine("Latency (ms):");
        sb.AppendLine($"  Min: {MinLatencyMs}");
        sb.AppendLine($"  Avg: {AverageLatencyMs:F3}");
        sb.AppendLine($"  P95: {P95LatencyMs:F3}");
        sb.AppendLine($"  P99: {P99LatencyMs:F3}");
        sb.AppendLine($"  Max: {MaxLatencyMs}");
        sb.AppendLine();
        sb.AppendLine($"Uptime: {TimeSpan.FromMilliseconds(UptimeMs):hh\\:mm\\:ss}");
        sb.AppendLine("==================================");
        return sb.ToString();
    }
}
