using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
using EonaCat.LogStack.Core;

namespace EonaCat.LogStack.Memory;

// 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>
/// Memory optimization utilities for zero-allocation logging paths
/// </summary>
public static class MemoryOptimizations
{
    /// <summary>
    /// Rents a scoped array from ArrayPool for using within a scope
    /// </summary>
    public static ScopedArray<T> RentArray<T>(int minimumLength)
    {
        return new ScopedArray<T>(ArrayPool<T>.Shared.Rent(minimumLength));
    }

    /// <summary>
    /// Gets size estimate for a log event
    /// </summary>
    public static int EstimateLogEventSize(in LogEvent logEvent)
    {
        int size = 64; // Base overhead

        // Timestamp, level, thread ID
        size += 24;

        // Message
        size += logEvent.Message.Length * 2;

        // Category
        size += (logEvent.Category?.Length ?? 0) * 2;

        // Exception (estimated)
        if (logEvent.HasException)
        {
            size += 1024;
        }

        // Properties
        if (logEvent.HasProperties)
        {
            size += logEvent.Properties.Count * 64;
        }

        return size;
    }

    /// <summary>
    /// Checks if memory usage is within acceptable range
    /// </summary>
    public static (bool acceptable, double pressure) CheckMemoryPressure(
        long thresholdBytes = 100 * 1024 * 1024)
    {
        long current = GC.GetTotalMemory(false);
        double pressure = (double)current / thresholdBytes;
        return (pressure < 0.8, pressure);
    }

    /// <summary>
    /// Attempts to reduce memory usage
    /// </summary>
    public static void TryCompact()
    {
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, false);
        GC.WaitForPendingFinalizers();
    }

    /// <summary>
    /// Gets current memory statistics
    /// </summary>
    public static MemoryStatistics GetStatistics()
    {
        return new MemoryStatistics
        {
            TotalMemory = GC.GetTotalMemory(false),
            Gen0Collections = GC.CollectionCount(0),
            Gen1Collections = GC.CollectionCount(1),
            Gen2Collections = GC.CollectionCount(2)
        };
    }
}

/// <summary>
/// Scoped array rental from ArrayPool
/// </summary>
public struct ScopedArray<T> : IDisposable
{
    private T[]? _array;
    private readonly int _length;

    public ScopedArray(int length)
    {
        _array = ArrayPool<T>.Shared.Rent(length);
        _length = length;
    }

    public ScopedArray(T[] array)
    {
        _array = array;
        _length = array.Length;
    }

    public Span<T> AsSpan() => new Span<T>(_array, 0, _length);
    public Memory<T> AsMemory() => new Memory<T>(_array, 0, _length);
    public T[] Array => _array!;

    public void Dispose()
    {
        if (_array != null)
        {
            ArrayPool<T>.Shared.Return(_array, false);
            _array = null;
        }
    }
}

/// <summary>
/// Memory statistics
/// </summary>
public class MemoryStatistics
{
    public long TotalMemory { get; set; }
    public int Gen0Collections { get; set; }
    public int Gen1Collections { get; set; }
    public int Gen2Collections { get; set; }

    public long TotalCollections => Gen0Collections + Gen1Collections + Gen2Collections;
}

/// <summary>
/// String interpolation helper for zero-allocation string building
/// </summary>
public struct ZeroAllocString
{
    private readonly StringBuilder _sb;
    private readonly bool _ownsBuilder;

    public ZeroAllocString(StringBuilder? builder = null)
    {
        if (builder == null)
        {
            _sb = new StringBuilder(256);
            _ownsBuilder = true;
        }
        else
        {
            _sb = builder;
            _ownsBuilder = false;
        }
    }

    public void Append(string? value)
    {
        _sb.Append(value);
    }

    public void Append(int value)
    {
        _sb.Append(value);
    }

    public void Append(long value)
    {
        _sb.Append(value);
    }

    public void Append(double value)
    {
        _sb.Append(value);
    }

    public void Append(char value)
    {
        _sb.Append(value);
    }

    public void AppendLine()
    {
        _sb.AppendLine();
    }

    public override string ToString()
    {
        return _sb.ToString();
    }

    public void Dispose()
    {
        if (_ownsBuilder)
        {
            _sb.Clear();
        }
    }
}

/// <summary>
/// Value type cache for frequently-used strings
/// </summary>
public sealed class StringCache
{
    private readonly Dictionary<string, string> _cache;
    private readonly int _maxSize;
    private readonly object _lock = new();

    public StringCache(int maxSize = 1000)
    {
        _maxSize = maxSize;
        _cache = new Dictionary<string, string>(maxSize);
    }

    /// <summary>
    /// Gets or caches a string
    /// </summary>
    public string Intern(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            return value;
        }

        lock (_lock)
        {
            if (_cache.TryGetValue(value, out var cached))
            {
                return cached;
            }

            if (_cache.Count < _maxSize)
            {
                _cache[value] = value;
                return value;
            }

            return value;
        }
    }

    /// <summary>
    /// Clears the cache
    /// </summary>
    public void Clear()
    {
        lock (_lock)
        {
            _cache.Clear();
        }
    }

    /// <summary>
    /// Gets cache statistics
    /// </summary>
    public (int count, int maxSize) GetStatistics()
    {
        lock (_lock)
        {
            return (_cache.Count, _maxSize);
        }
    }
}

/// <summary>
/// Memory-efficient concurrent bag alternative
/// </summary>
public sealed class EfficientObjectPool<T> where T : class
{
    private readonly Stack<T> _stack;
    private readonly Func<T> _factory;
    private int _count;
    private readonly int _maxSize;
    private readonly object _lock = new();

    public EfficientObjectPool(Func<T> factory, int maxSize = 100)
    {
        _factory = factory;
        _maxSize = maxSize;
        _stack = new Stack<T>(maxSize);
    }

    public T Rent()
    {
        lock (_lock)
        {
            return _stack.Count > 0 ? _stack.Pop() : _factory();
        }
    }

    public void Return(T item)
    {
        if (item == null)
        {
            return;
        }

        lock (_lock)
        {
            if (_count < _maxSize)
            {
                _stack.Push(item);
                _count++;
            }
        }
    }

    public int Count
    {
        get
        {
            lock (_lock)
            {
                return _count;
            }
        }
    }

    public void Clear()
    {
        lock (_lock)
        {
            _stack.Clear();
            _count = 0;
        }
    }
}
