using System;
using System.Collections.Generic;
using System.Linq;
using EonaCat.LogStack.Core;

namespace EonaCat.LogStack.Features
{
    /// <summary>
    /// High-performance in-memory log search and query engine with fluent API.
    /// Indexes logs for fast retrieval and supports complex queries.
    /// </summary>
    public class LogSearchEngine
    {
        private readonly List<LogSearchEntry> _logIndex = new();
        private readonly int _maxEntries;
        private readonly object _lockObject = new();
        private bool _isEnabled = true;

        public LogSearchEngine(int maxIndexSize = 10000)
        {
            _maxEntries = maxIndexSize > 0 ? maxIndexSize : 10000;
        }

        /// <summary>
        /// Adds a log entry to the search index.
        /// </summary>
        public void IndexLog(LogEvent logEvent, string formattedMessage)
        {
            lock (_lockObject)
            {
                // Maintain max size with FIFO removal
                if (_logIndex.Count >= _maxEntries)
                {
                    _logIndex.RemoveAt(0);
                }

                _logIndex.Add(new LogSearchEntry
                {
                    Timestamp = DateTime.UtcNow,
                    Level = logEvent.Level,
                    Logger = logEvent.Category ?? "Default",
                    Message = formattedMessage?.Trim() ?? string.Empty,
                    Exception = logEvent.Exception?.Message ?? string.Empty,
                    CorrelationId = logEvent.CustomData?? string.Empty,
                    Properties = logEvent.Properties != null 
                        ? new Dictionary<string, object>(logEvent.Properties.Where(x => x.Value != null).ToDictionary(x => x.Key, x => x.Value))
                        : new Dictionary<string, object>()
                });
            }
        }

        /// <summary>
        /// Enables or disables indexing.
        /// </summary>
        public void SetIndexingEnabled(bool enabled)
        {
            _isEnabled = enabled;
        }

        /// <summary>
        /// Clears all indexed logs.
        /// </summary>
        public void Clear()
        {
            lock (_lockObject)
            {
                _logIndex.Clear();
            }
        }

        /// <summary>
        /// Returns the total number of indexed logs.
        /// </summary>
        public int Count
        {
            get
            {
                lock (_lockObject)
                {
                    return _logIndex.Count;
                }
            }
        }

        /// <summary>
        /// Searches logs by keyword (searches message and exception fields).
        /// </summary>
        public IEnumerable<LogSearchEntry> SearchByKeyword(string keyword)
        {
            if (string.IsNullOrWhiteSpace(keyword))
                return Enumerable.Empty<LogSearchEntry>();

            var lower = keyword.ToLowerInvariant();
            lock (_lockObject)
            {
                return _logIndex
                    .Where(e => e.Message.Contains(lower, StringComparison.OrdinalIgnoreCase) ||
                                e.Exception.Contains(lower, StringComparison.OrdinalIgnoreCase))
                    .ToList();
            }
        }

        /// <summary>
        /// Searches logs by logger name (partial match).
        /// </summary>
        public IEnumerable<LogSearchEntry> SearchByLogger(string loggerName)
        {
            if (string.IsNullOrWhiteSpace(loggerName))
                return Enumerable.Empty<LogSearchEntry>();

            lock (_lockObject)
            {
                return _logIndex
                    .Where(e => e.Logger.Contains(loggerName, StringComparison.OrdinalIgnoreCase))
                    .ToList();
            }
        }

        /// <summary>
        /// Searches logs by correlation ID.
        /// </summary>
        public IEnumerable<LogSearchEntry> SearchByCorrelationId(string correlationId)
        {
            if (string.IsNullOrWhiteSpace(correlationId))
                return Enumerable.Empty<LogSearchEntry>();

            lock (_lockObject)
            {
                return _logIndex
                    .Where(e => e.CorrelationId.Equals(correlationId, StringComparison.OrdinalIgnoreCase))
                    .ToList();
            }
        }

        /// <summary>
        /// Searches logs by log level.
        /// </summary>
        public IEnumerable<LogSearchEntry> SearchByLevel(LogLevel level)
        {
            lock (_lockObject)
            {
                return _logIndex
                    .Where(e => e.Level == level)
                    .ToList();
            }
        }

        /// <summary>
        /// Searches logs within a time range.
        /// </summary>
        public IEnumerable<LogSearchEntry> SearchByTimeRange(DateTime fromUtc, DateTime toUtc)
        {
            if (fromUtc > toUtc)
                throw new ArgumentException("fromUtc must be less than or equal to toUtc");

            lock (_lockObject)
            {
                return _logIndex
                    .Where(e => e.Timestamp >= fromUtc && e.Timestamp <= toUtc)
                    .ToList();
            }
        }

        /// <summary>
        /// Searches logs with multiple criteria (AND logic).
        /// </summary>
        public IEnumerable<LogSearchEntry> Search(LogSearchQuery query)
        {
            if (query == null)
                return Enumerable.Empty<LogSearchEntry>();

            lock (_lockObject)
            {
                var results = _logIndex.AsEnumerable();

                if (!string.IsNullOrWhiteSpace(query.Keyword))
                {
                    var lower = query.Keyword.ToLowerInvariant();
                    results = results.Where(e =>
                        e.Message.Contains(lower, StringComparison.OrdinalIgnoreCase) ||
                        e.Exception.Contains(lower, StringComparison.OrdinalIgnoreCase));
                }

                if (query.Level.HasValue)
                {
                    results = results.Where(e => e.Level == query.Level.Value);
                }

                if (!string.IsNullOrWhiteSpace(query.LoggerName))
                {
                    results = results.Where(e =>
                        e.Logger.Contains(query.LoggerName, StringComparison.OrdinalIgnoreCase));
                }

                if (!string.IsNullOrWhiteSpace(query.CorrelationId))
                {
                    results = results.Where(e =>
                        e.CorrelationId.Equals(query.CorrelationId, StringComparison.OrdinalIgnoreCase));
                }

                if (query.FromUtc.HasValue)
                {
                    results = results.Where(e => e.Timestamp >= query.FromUtc.Value);
                }

                if (query.ToUtc.HasValue)
                {
                    results = results.Where(e => e.Timestamp <= query.ToUtc.Value);
                }

                // Apply sorting
                switch (query.SortBy)
                {
                    case LogSearchSortBy.TimestampDescending:
                        results = results.OrderByDescending(e => e.Timestamp);
                        break;
                    case LogSearchSortBy.TimestampAscending:
                        results = results.OrderBy(e => e.Timestamp);
                        break;
                    case LogSearchSortBy.Level:
                        results = results.OrderBy(e => e.Level);
                        break;
                    case LogSearchSortBy.Logger:
                        results = results.OrderBy(e => e.Logger);
                        break;
                }

                // Apply limit
                if (query.Limit > 0)
                {
                    results = results.Take(query.Limit);
                }

                return results.ToList();
            }
        }

        /// <summary>
        /// Gets recent logs (last N entries).
        /// </summary>
        public IEnumerable<LogSearchEntry> GetRecent(int count = 100)
        {
            if (count <= 0)
                count = 100;

            lock (_lockObject)
            {
                return _logIndex
                    .AsEnumerable()
                    .Skip(_logIndex.Count > count ? _logIndex.Count - count : 0)
                    .Reverse()
                    .ToList();
            }
        }

        /// <summary>
        /// Gets statistics about indexed logs.
        /// </summary>
        public LogSearchStatistics GetStatistics()
        {
            lock (_lockObject)
            {
                if (_logIndex.Count == 0)
                    return new LogSearchStatistics();

                return new LogSearchStatistics
                {
                    TotalLogs = _logIndex.Count,
                    Errors = _logIndex.Count(e => e.Level == LogLevel.Error),
                    Warnings = _logIndex.Count(e => e.Level == LogLevel.Warning),
                    Infos = _logIndex.Count(e => e.Level == LogLevel.Information),
                    OldestLog = _logIndex.First().Timestamp,
                    NewestLog = _logIndex.Last().Timestamp,
                    UniqueLooggers = _logIndex.Select(e => e.Logger).Distinct().Count(),
                    UniqueCorrelationIds = _logIndex
                        .Where(e => !string.IsNullOrEmpty(e.CorrelationId))
                        .Select(e => e.CorrelationId)
                        .Distinct()
                        .Count()
                };
            }
        }
    }

    /// <summary>
    /// Represents a single indexed log entry.
    /// </summary>
    public class LogSearchEntry
    {
        public DateTime Timestamp { get; set; }
        public LogLevel Level { get; set; }
        public string Logger { get; set; }
        public string Message { get; set; }
        public string Exception { get; set; }
        public string CorrelationId { get; set; }
        public Dictionary<string, object> Properties { get; set; }
    }

    /// <summary>
    /// Query builder for log searches.
    /// </summary>
    public class LogSearchQuery
    {
        public string Keyword { get; set; }
        public LogLevel? Level { get; set; }
        public string LoggerName { get; set; }
        public string CorrelationId { get; set; }
        public DateTime? FromUtc { get; set; }
        public DateTime? ToUtc { get; set; }
        public LogSearchSortBy SortBy { get; set; } = LogSearchSortBy.TimestampDescending;
        public int Limit { get; set; } = 0; // 0 = no limit
    }

    /// <summary>
    /// Sort options for log searches.
    /// </summary>
    public enum LogSearchSortBy
    {
        None = 0,
        TimestampAscending = 1,
        TimestampDescending = 2,
        Level = 3,
        Logger = 4
    }

    /// <summary>
    /// Statistics about indexed logs.
    /// </summary>
    public class LogSearchStatistics
    {
        public int TotalLogs { get; set; }
        public int Errors { get; set; }
        public int Warnings { get; set; }
        public int Infos { get; set; }
        public DateTime OldestLog { get; set; }
        public DateTime NewestLog { get; set; }
        public int UniqueLooggers { get; set; }
        public int UniqueCorrelationIds { get; set; }
    }
}
