using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using EonaCat.LogStack.Core;

namespace EonaCat.LogStack.Features
{
    /// <summary>
    /// Captures and replays log sequences for debugging and analysis.
    /// Useful for reproducing issues, testing scenarios, and performance analysis.
    /// </summary>
    public class LogReplayEngine
    {
        private readonly List<CapturedLogEntry> _capturedLogs = new();
        private readonly object _lockObject = new();
        private bool _isCapturing = false;
        private string _currentCaptureSessionId = string.Empty;

        /// <summary>
        /// Starts a new log capture session.
        /// </summary>
        public string StartCapture(string sessionName = null)
        {
            lock (_lockObject)
            {
                _currentCaptureSessionId = sessionName ?? $"session_{Guid.NewGuid().ToString("N").Substring(0, 8)}";
                _isCapturing = true;
                _capturedLogs.Clear();
                return _currentCaptureSessionId;
            }
        }

        /// <summary>
        /// Stops capturing logs.
        /// </summary>
        public void StopCapture()
        {
            lock (_lockObject)
            {
                _isCapturing = false;
            }
        }

        /// <summary>
        /// Captures a log entry.
        /// </summary>
        public void CaptureLog(LogEvent logEvent, string formattedMessage)
        {
            if (!_isCapturing)
                return;

            lock (_lockObject)
            {
                _capturedLogs.Add(new CapturedLogEntry
                {
                    SessionId = _currentCaptureSessionId,
                    Timestamp = DateTime.UtcNow,
                    Level = logEvent.Level,
                    Logger = logEvent.Category ?? "Default",
                    Message = formattedMessage?.Trim() ?? string.Empty,
                    Exception = logEvent.Exception?.ToString() ?? string.Empty,
                    CorrelationId = logEvent.CustomData ?? string.Empty,
                    Properties = logEvent.Properties != null
                        ? new Dictionary<string, string>(
                            logEvent.Properties.Where(x => x.Value != null).ToDictionary(
                                kvp => kvp.Key,
                                kvp => kvp.Value.ToString() ?? string.Empty))
                        : new Dictionary<string, string>()
                });
            }
        }

        /// <summary>
        /// Gets all captured logs in current session.
        /// </summary>
        public IEnumerable<CapturedLogEntry> GetCapturedLogs()
        {
            lock (_lockObject)
            {
                return _capturedLogs.ToList();
            }
        }

        /// <summary>
        /// Gets captured logs filtered by criteria.
        /// </summary>
        public IEnumerable<CapturedLogEntry> GetCapturedLogs(LogLevel? minLevel = null, 
            string loggerFilter = null, DateTime? fromUtc = null, DateTime? toUtc = null)
        {
            lock (_lockObject)
            {
                var results = _capturedLogs.AsEnumerable();

                if (minLevel.HasValue)
                    results = results.Where(l => l.Level >= minLevel.Value);

                if (!string.IsNullOrWhiteSpace(loggerFilter))
                    results = results.Where(l =>
                        l.Logger.Contains(loggerFilter, StringComparison.OrdinalIgnoreCase));

                if (fromUtc.HasValue)
                    results = results.Where(l => l.Timestamp >= fromUtc.Value);

                if (toUtc.HasValue)
                    results = results.Where(l => l.Timestamp <= toUtc.Value);

                return results.ToList();
            }
        }

        /// <summary>
        /// Exports captured logs to JSON file.
        /// </summary>
        public void ExportToJson(string filePath)
        {
            lock (_lockObject)
            {
                var json = JsonSerializer.Serialize(_capturedLogs, new JsonSerializerOptions
                {
                    WriteIndented = true,
                    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
                });

                File.WriteAllText(filePath, json);
            }
        }

        /// <summary>
        /// Imports captured logs from JSON file.
        /// </summary>
        public void ImportFromJson(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException($"File not found: {filePath}");

            var json = File.ReadAllText(filePath);
            var logs = JsonSerializer.Deserialize<List<CapturedLogEntry>>(json);

            lock (_lockObject)
            {
                _capturedLogs.AddRange(logs ?? new List<CapturedLogEntry>());
            }
        }

        /// <summary>
        /// Replays captured logs with optional delays.
        /// </summary>
        public void Replay(Action<CapturedLogEntry> onLogReplayed, bool respectTimings = false)
        {
            if (onLogReplayed == null)
                throw new ArgumentNullException(nameof(onLogReplayed));

            List<CapturedLogEntry> logsToReplay;

            lock (_lockObject)
            {
                logsToReplay = _capturedLogs.ToList();
            }

            if (logsToReplay.Count == 0)
                return;

            DateTime? previousTimestamp = null;

            foreach (var log in logsToReplay)
            {
                if (respectTimings && previousTimestamp.HasValue)
                {
                    var delay = (long)(log.Timestamp - previousTimestamp.Value).TotalMilliseconds;
                    if (delay > 0)
                    {
                        System.Threading.Thread.Sleep((int)Math.Min(delay, int.MaxValue));
                    }
                }

                onLogReplayed(log);
                previousTimestamp = log.Timestamp;
            }
        }

        /// <summary>
        /// Gets statistics about captured logs.
        /// </summary>
        public LogReplayStatistics GetStatistics()
        {
            lock (_lockObject)
            {
                if (_capturedLogs.Count == 0)
                    return new LogReplayStatistics();

                var byLevel = _capturedLogs.GroupBy(l => l.Level)
                    .ToDictionary(g => g.Key, g => g.Count());

                var duration = _capturedLogs.Max(l => l.Timestamp) - _capturedLogs.Min(l => l.Timestamp);

                return new LogReplayStatistics
                {
                    TotalCaptured = _capturedLogs.Count,
                    SessionId = _currentCaptureSessionId,
                    StartTime = _capturedLogs.First().Timestamp,
                    EndTime = _capturedLogs.Last().Timestamp,
                    DurationSeconds = duration.TotalSeconds,
                    LogsByLevel = byLevel,
                    UniqueLoggers = _capturedLogs.Select(l => l.Logger).Distinct().Count(),
                    HasExceptions = _capturedLogs.Any(l => !string.IsNullOrEmpty(l.Exception))
                };
            }
        }

        /// <summary>
        /// Clears all captured logs.
        /// </summary>
        public void Clear()
        {
            lock (_lockObject)
            {
                _capturedLogs.Clear();
                _isCapturing = false;
            }
        }

        /// <summary>
        /// Gets detailed replay scenario for testing.
        /// </summary>
        public LogReplayScenario CreateScenario(string name)
        {
            return new LogReplayScenario
            {
                Name = name,
                CreatedAt = DateTime.UtcNow,
                Logs = GetCapturedLogs().ToList()
            };
        }

        /// <summary>
        /// Finds similar log patterns for diagnosis.
        /// </summary>
        public IEnumerable<LogPattern> FindPatterns(int minOccurrences = 2)
        {
            lock (_lockObject)
            {
                var patterns = _capturedLogs
                    .GroupBy(l => l.Logger)
                    .SelectMany(g =>
                        g.GroupBy(l => l.Level)
                        .SelectMany(lg => new[]
                        {
                            new LogPattern
                            {
                                Logger = g.Key,
                                Level = lg.Key,
                                Count = lg.Count(),
                                FirstOccurrence = lg.Min(l => l.Timestamp),
                                LastOccurrence = lg.Max(l => l.Timestamp)
                            }
                        }))
                    .Where(p => p.Count >= minOccurrences)
                    .OrderByDescending(p => p.Count);

                return patterns.ToList();
            }
        }
    }

    /// <summary>
    /// Represents a single captured log entry.
    /// </summary>
    public class CapturedLogEntry
    {
        public string SessionId { get; set; }
        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, string> Properties { get; set; }
    }

    /// <summary>
    /// Statistics about captured logs.
    /// </summary>
    public class LogReplayStatistics
    {
        public int TotalCaptured { get; set; }
        public string SessionId { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public double DurationSeconds { get; set; }
        public Dictionary<LogLevel, int> LogsByLevel { get; set; } = new();
        public int UniqueLoggers { get; set; }
        public bool HasExceptions { get; set; }
    }

    /// <summary>
    /// Represents a pre-configured log replay scenario.
    /// </summary>
    public class LogReplayScenario
    {
        public string Name { get; set; }
        public DateTime CreatedAt { get; set; }
        public string Description { get; set; }
        public List<CapturedLogEntry> Logs { get; set; } = new();

        public void Save(string filePath)
        {
            var json = JsonSerializer.Serialize(this, new JsonSerializerOptions
            {
                WriteIndented = true
            });
            File.WriteAllText(filePath, json);
        }

        public static LogReplayScenario Load(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException($"Scenario file not found: {filePath}");

            var json = File.ReadAllText(filePath);
            return JsonSerializer.Deserialize<LogReplayScenario>(json);
        }
    }

    /// <summary>
    /// Represents a detected log pattern.
    /// </summary>
    public class LogPattern
    {
        public string Logger { get; set; }
        public LogLevel Level { get; set; }
        public int Count { get; set; }
        public DateTime FirstOccurrence { get; set; }
        public DateTime LastOccurrence { get; set; }
    }
}
