using EonaCat.LogStack.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

namespace EonaCat.LogStack.Flows
{
    // 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>
    /// LocalStorageQueryFlow stores recent log events in memory and provides LINQ-like querying capabilities.
    /// Useful for diagnostics, testing, and real-time log analysis without external storage.
    /// Uses a circular buffer with configurable maximum size to prevent unbounded memory growth.
    /// </summary>
    public sealed class LocalStorageQueryFlow : FlowBase
    {
        private readonly int _maxCapacity;
        private readonly ConcurrentQueue<LogEvent> _logBuffer = new();
        private int _currentCount = 0;

        /// <summary>
        /// Gets all stored log events
        /// </summary>
        public IReadOnlyList<LogEvent> AllEvents => _logBuffer.ToList();

        /// <summary>
        /// Gets the number of events currently stored
        /// </summary>
        public int StoredEventCount => _currentCount;

        /// <summary>
        /// Creates a new LocalStorageQueryFlow with specified capacity
        /// </summary>
        /// <param name="maxCapacity">Maximum number of log events to keep in memory (default 10000)</param>
        /// <param name="minimumLevel">Minimum log level to store</param>
        public LocalStorageQueryFlow(
            int maxCapacity = 10000,
            LogLevel minimumLevel = LogLevel.Trace)
            : base("LocalStorageQuery", minimumLevel)
        {
            if (maxCapacity <= 0)
                throw new ArgumentException("Capacity must be greater than 0", nameof(maxCapacity));

            _maxCapacity = maxCapacity;
        }

        public override async Task<WriteResult> BlastAsync(LogEvent logEvent, CancellationToken cancellationToken = default)
        {
            if (!IsEnabled || !IsLogLevelEnabled(logEvent))
                return WriteResult.Success;

            try
            {
                _logBuffer.Enqueue(logEvent);
                int newCount = Interlocked.Increment(ref _currentCount);

                // Maintain circular buffer by dropping oldest items when over capacity
                while (newCount > _maxCapacity)
                {
                    if (_logBuffer.TryDequeue(out _))
                    {
                        newCount = Interlocked.Decrement(ref _currentCount);
                    }
                    else
                    {
                        break;
                    }
                }

                return await Task.FromResult(WriteResult.Success);
            }
            catch (Exception ex)
            {
                return WriteResult.Failed;
            }
        }

        public override async Task<WriteResult> BlastBatchAsync(ReadOnlyMemory<LogEvent> logEvents, CancellationToken cancellationToken = default)
        {
            try
            {
                foreach (var logEvent in logEvents.Span)
                {
                    if (IsLogLevelEnabled(logEvent))
                    {
                        _logBuffer.Enqueue(logEvent);
                        int newCount = Interlocked.Increment(ref _currentCount);

                        // Maintain circular buffer
                        while (newCount > _maxCapacity)
                        {
                            if (_logBuffer.TryDequeue(out _))
                            {
                                newCount = Interlocked.Decrement(ref _currentCount);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }

                return await Task.FromResult(WriteResult.Success);
            }
            catch (Exception ex)
            {
                return WriteResult.Failed;
            }
        }

        public override async Task FlushAsync(CancellationToken cancellationToken = default)
        {
            // No async I/O needed for in-memory storage
            await Task.CompletedTask;
        }

        /// <summary>
        /// Query stored events by log level
        /// </summary>
        public IEnumerable<LogEvent> QueryByLevel(LogLevel level)
        {
            return _logBuffer.Where(e => e.Level == level);
        }

        /// <summary>
        /// Query stored events by category
        /// </summary>
        public IEnumerable<LogEvent> QueryByCategory(string category)
        {
            return _logBuffer.Where(e => e.Category?.Equals(category, StringComparison.OrdinalIgnoreCase) ?? false);
        }

        /// <summary>
        /// Query stored events by message pattern
        /// </summary>
        public IEnumerable<LogEvent> QueryByMessagePattern(string pattern, StringComparison comparison = StringComparison.OrdinalIgnoreCase)
        {
            return _logBuffer.Where(e => 
            {
                var msg = e.Message.ToString();
                return msg.Contains(pattern, comparison);
            });
        }

        /// <summary>
        /// Query stored events by time range
        /// </summary>
        public IEnumerable<LogEvent> QueryByTimeRange(DateTime from, DateTime to)
        {
            var fromTicks = from.Ticks;
            var toTicks = to.Ticks;
            return _logBuffer.Where(e => e.Timestamp >= fromTicks && e.Timestamp <= toTicks);
        }

        /// <summary>
        /// Query stored events that contain exceptions
        /// </summary>
        public IEnumerable<LogEvent> QueryExceptionEvents()
        {
            return _logBuffer.Where(e => e.Exception != null);
        }

        /// <summary>
        /// Query stored events by duration (for performance logs)
        /// </summary>
        public IEnumerable<LogEvent> QueryByMinimumDuration(TimeSpan duration)
        {
            return _logBuffer.Where(e => 
                e.Properties != null && 
                e.Properties.TryGetValue("duration", out var durValue) && 
                durValue is TimeSpan ts && 
                ts >= duration);
        }

        /// <summary>
        /// Get statistics about stored events
        /// </summary>
        public LogStorageStatistics GetStatistics()
        {
            var events = _logBuffer.ToList();
            if (events.Count == 0)
                return new LogStorageStatistics();

            var stats = new LogStorageStatistics
            {
                TotalEvents = events.Count,
                EventsByLevel = events
                    .GroupBy(e => e.Level)
                    .ToDictionary(g => g.Key, g => g.Count()),
                EventsByCategory = events
                    .GroupBy(e => e.Category ?? "Unknown")
                    .ToDictionary(g => g.Key, g => g.Count()),
                OldestEventTime = new DateTime(events.Min(e => e.Timestamp)),
                NewestEventTime = new DateTime(events.Max(e => e.Timestamp)),
                ExceptionCount = events.Count(e => e.Exception != null),
                AverageMessageLength = (int)events.Average(e => e.Message.Length)
            };

            return stats;
        }

        /// <summary>
        /// Clear all stored events
        /// </summary>
        public void Clear()
        {
            while (_logBuffer.TryDequeue(out _)) { }
            _currentCount = 0;
        }

        /// <summary>
        /// Get the most recent N events
        /// </summary>
        public IEnumerable<LogEvent> GetLatest(int count)
        {
            return _logBuffer.Reverse().Take(count);
        }

        /// <summary>
        /// Search with custom predicate
        /// </summary>
        public IEnumerable<LogEvent> Search(Func<LogEvent, bool> predicate)
        {
            return _logBuffer.Where(predicate);
        }

        public override async ValueTask DisposeAsync()
        {
            Clear();
            await base.DisposeAsync();
        }
    }

    /// <summary>
    /// Statistics about logs stored in LocalStorageQueryFlow
    /// </summary>
    public class LogStorageStatistics
    {
        public int TotalEvents { get; set; }
        public Dictionary<LogLevel, int> EventsByLevel { get; set; } = new();
        public Dictionary<string, int> EventsByCategory { get; set; } = new();
        public DateTime OldestEventTime { get; set; }
        public DateTime NewestEventTime { get; set; }
        public int ExceptionCount { get; set; }
        public int AverageMessageLength { get; set; }

        public override string ToString()
        {
            var levelBreakdown = string.Join(", ", EventsByLevel.Select(kvp => $"{kvp.Key}: {kvp.Value}"));
            var categoryBreakdown = string.Join(", ", EventsByCategory.Take(3).Select(kvp => $"{kvp.Key}: {kvp.Value}"));

            return $"LogStorageStats: Total={TotalEvents}, Exceptions={ExceptionCount}, " +
                   $"Levels=[{levelBreakdown}], Categories=[{categoryBreakdown}]";
        }
    }
}
