using EonaCat.LogStack.Core;
using System;
using System.Diagnostics;
using System.Threading;

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

/// <summary>
/// Adaptive sampling engine that dynamically adjusts sampling rates based on log volume and system resources.
/// Superior to static sampling as it prevents log loss during normal operation while reducing overhead during peaks.
/// </summary>
public class AdaptiveSamplingEngine
{
    private double _samplingRate = 1.0; // Sample 100% of logs by default
    private double _targetThroughput = 10000; // Target 10k logs/sec
    private long _logCount;
    private long _lastAdjustmentTicks;
    private readonly Stopwatch _perfCounter = Stopwatch.StartNew();
    private readonly double _adjustmentIntervalMs;
    private readonly SamplingStrategy _strategy;
    private readonly object _statsLock = new object();

    // CPU and Memory monitoring
    private double _currentCpuPercent;
    private double _currentMemoryPercent;
    private readonly double _cpuThreshold;
    private readonly double _memoryThreshold;
    private readonly Process _currentProcess = Process.GetCurrentProcess();
    private DateTime _lastCpuCheck;

    public AdaptiveSamplingEngine(
        SamplingStrategy strategy = SamplingStrategy.Throughput,
        double targetThroughput = 10000,
        double adjustmentIntervalMs = 5000,
        double cpuThreshold = 0.80,
        double memoryThreshold = 0.85)
    {
        _strategy = strategy;
        _targetThroughput = targetThroughput;
        _adjustmentIntervalMs = adjustmentIntervalMs;
        _cpuThreshold = cpuThreshold;
        _memoryThreshold = memoryThreshold;
        _lastAdjustmentTicks = _perfCounter.ElapsedTicks;
        _lastCpuCheck = DateTime.UtcNow;
    }

    /// <summary>
    /// Determines if a log event should be sampled (kept) or dropped.
    /// Returns true if the event should be logged.
    /// </summary>
    public bool ShouldSample()
    {
        // Periodically adjust sampling rate
        lock (_statsLock)
        {
            if (_perfCounter.ElapsedTicks - _lastAdjustmentTicks > 
                TicksFromMilliseconds(_adjustmentIntervalMs))
            {
                AdjustSamplingRate();
            }
        }

        // Use random sampling based on current rate
        if (_samplingRate >= 1.0)
            return true;

        if (_samplingRate <= 0.0)
            return false;

        return ThreadSafeRandom.NextDouble() < _samplingRate;
    }

    /// <summary>
    /// Adjusts the sampling rate based on current metrics and strategy.
    /// </summary>
    private void AdjustSamplingRate()
    {
        _lastAdjustmentTicks = _perfCounter.ElapsedTicks;
        var currentLogCount = Interlocked.Exchange(ref _logCount, 0);

        UpdateResourceMetrics();

        switch (_strategy)
        {
            case SamplingStrategy.Throughput:
                AdjustForThroughput();
                break;
            case SamplingStrategy.Resources:
                AdjustForResources();
                break;
            case SamplingStrategy.Hybrid:
                AdjustForThroughput();
                AdjustForResources();
                break;
            case SamplingStrategy.Adaptive:
                AdjustAdaptive();
                break;
        }

        // Clamp between 0 and 1
        _samplingRate = Math.Max(0.0, Math.Min(1.0, _samplingRate));
    }

    private void AdjustForThroughput()
    {
        var elapsedSeconds = _adjustmentIntervalMs / 1000.0;
        var currentLogCount = 0L;
        lock (_statsLock)
        {
            currentLogCount = Interlocked.Read(ref _logCount);
        }
        var actualThroughput = currentLogCount / elapsedSeconds;

        if (actualThroughput > _targetThroughput * 1.2) // 20% over target
        {
            // Reduce sampling rate
            _samplingRate *= 0.9;
        }
        else if (actualThroughput < _targetThroughput * 0.8) // 20% under target
        {
            // Increase sampling rate
            _samplingRate *= 1.1;
        }
    }

    private void AdjustForResources()
    {
        if (_currentCpuPercent > _cpuThreshold || _currentMemoryPercent > _memoryThreshold)
        {
            // High resource usage - reduce sampling rate more aggressively
            _samplingRate *= 0.7;
        }
        else if (_currentCpuPercent < _cpuThreshold * 0.5 && _currentMemoryPercent < _memoryThreshold * 0.5)
        {
            // Low resource usage - increase sampling rate
            _samplingRate *= 1.15;
        }
    }

    private void AdjustAdaptive()
    {
        /* Adaptive strategy: adjust based on both throughput and resources,
           with memory being more critical than CPU */
        var cpuFactor = _currentCpuPercent / _cpuThreshold;
        var memoryFactor = _currentMemoryPercent / _memoryThreshold;

        // Memory pressure has higher weight
        var resourcePressure = (cpuFactor * 0.4) + (memoryFactor * 0.6);

        if (resourcePressure > 1.0)
        {
            _samplingRate *= (1.0 - (resourcePressure - 1.0) * 0.5);
        }
        else if (resourcePressure < 0.5)
        {
            _samplingRate *= 1.1;
        }
    }

    private void UpdateResourceMetrics()
    {
        var now = DateTime.UtcNow;
        if ((now - _lastCpuCheck).TotalMilliseconds < 1000)
            return; // Update at most once per second to avoid overhead

        _lastCpuCheck = now;

        try
        {
            // CPU usage (process)
            _currentCpuProcess = _currentProcess.TotalProcessorTime.TotalMilliseconds;

            // Simplified: estimate CPU % based on processor count
            var cpuUsageEstimate = Math.Min(1.0, _currentCpuProcess / (Environment.ProcessorCount * 100));
            _currentCpuPercent = cpuUsageEstimate;

            // Memory usage
            var totalMemory = GC.GetTotalMemory(false);
            var workingSet = _currentProcess.WorkingSet64;

            // Estimate based on available system memory
            // In production, use MemoryMarshal or other APIs for more precise data
            _currentMemoryPercent = Math.Min(1.0, workingSet / (8.0 * 1024 * 1024 * 1024)); // Assume 8GB
        }
        catch
        {
            // If we can't get metrics, don't crash
        }
    }

    private double _currentCpuProcess;

    /// <summary>
    /// Sets the target throughput for throughput-based sampling.
    /// </summary>
    public void SetTargetThroughput(double logsPerSecond)
    {
        _targetThroughput = Math.Max(1, logsPerSecond);
    }

    /// <summary>
    /// Gets current sampling rate (0.0 to 1.0).
    /// </summary>
    public double GetSamplingRate() => _samplingRate;

    /// <summary>
    /// Gets current resource utilization metrics.
    /// </summary>
    public SamplingMetrics GetMetrics()
    {
        lock (_statsLock)
        {
            return new SamplingMetrics
            {
                SamplingRate = _samplingRate,
                CurrentCpuPercent = _currentCpuPercent,
                CurrentMemoryPercent = _currentMemoryPercent,
                Strategy = _strategy,
                TargetThroughput = _targetThroughput,
                LogsProcessedInLastInterval = Interlocked.Read(ref _logCount)
            };
        }
    }

    /// <summary>
    /// Records a log event (increments counter for sampling decisions).
    /// </summary>
    public void RecordLogEvent()
    {
        Interlocked.Increment(ref _logCount);
    }

    private static long TicksFromMilliseconds(double ms)
    {
        return (long)(ms * Stopwatch.Frequency / 1000.0);
    }
}

/// <summary>
/// Sampling strategies for adaptive engine.
/// </summary>
public enum SamplingStrategy
{
    /// <summary>
    /// Adjusts sampling based on throughput to match target logs/sec
    /// </summary>
    Throughput,

    /// <summary>
    /// Adjusts sampling based on CPU and memory usage
    /// </summary>
    Resources,

    /// <summary>
    /// Combines throughput and resource monitoring
    /// </summary>
    Hybrid,

    /// <summary>
    /// Advanced adaptive strategy that learns optimal rate
    /// </summary>
    Adaptive
}

/// <summary>
/// Metrics from the adaptive sampling engine.
/// </summary>
public class SamplingMetrics
{
    /// <summary>
    /// Current sampling rate (0.0 = drop all, 1.0 = keep all)
    /// </summary>
    public double SamplingRate { get; set; }

    /// <summary>
    /// Current CPU usage percentage (0.0 to 1.0)
    /// </summary>
    public double CurrentCpuPercent { get; set; }

    /// <summary>
    /// Current memory usage percentage (0.0 to 1.0)
    /// </summary>
    public double CurrentMemoryPercent { get; set; }

    /// <summary>
    /// Active sampling strategy
    /// </summary>
    public SamplingStrategy Strategy { get; set; }

    /// <summary>
    /// Target throughput in logs per second
    /// </summary>
    public double TargetThroughput { get; set; }

    /// <summary>
    /// Logs processed in last adjustment interval
    /// </summary>
    public long LogsProcessedInLastInterval { get; set; }
}

/// <summary>
/// Thread-safe random number generator for sampling decisions.
/// </summary>
internal static class ThreadSafeRandom
{
    private static readonly ThreadLocal<Random> _random = new ThreadLocal<Random>(
        () => new Random(Guid.NewGuid().GetHashCode())
    );

    public static double NextDouble() => _random.Value.NextDouble();
}
