using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading.Channels;

namespace PolyTraderSharp.Services
{
    public enum LogLevel { Debug, Info, Warning, Error, Trade, TradeReasoning }

    public class LogMessageEventArgs : EventArgs
    {
        public string Message { get; }
        public LogLevel Level { get; }
        public DateTime Timestamp { get; }

        /// <summary>Korrelations-ID (z. B. SignalId) für die Log-Forensik; leer wenn ohne Kontext.</summary>
        public string CorrelationId { get; }

        public LogMessageEventArgs(string message, LogLevel level, string correlationId = "")
        {
            Message = message;
            Level = level;
            Timestamp = DateTime.Now;
            CorrelationId = correlationId ?? "";
        }
    }

    /// <summary>
    /// Reiner JSONL-Formatter für Log-Events (S-0, Supervisor-Konzept): eine JSON-Zeile je Event –
    /// append-fähig, streambar, maschinen-/KI-lesbar. Statisch und seiteneffektfrei → unit-getestet.
    /// </summary>
    public static class LogJson
    {
        public static string Format(LogMessageEventArgs e)
        {
            var obj = new
            {
                ts = e.Timestamp.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz"),
                level = e.Level.ToString(),
                cid = string.IsNullOrEmpty(e.CorrelationId) ? null : e.CorrelationId,
                msg = e.Message
            };
            return System.Text.Json.JsonSerializer.Serialize(obj,
                new System.Text.Json.JsonSerializerOptions
                {
                    Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
                    DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
                });
        }

        /// <summary>Geparste JSONL-Zeile für den Log Viewer.</summary>
        public sealed record ParsedLogLine(string Time, string Level, string Cid, string Message);

        /// <summary>
        /// Parst eine JSONL-Zeile (Gegenstück zu <see cref="Format"/>). Liefert null bei leeren/
        /// beschädigten Zeilen (der Viewer überspringt sie, statt zu brechen).
        /// </summary>
        public static ParsedLogLine? ParseLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return null;
            try
            {
                using var doc = System.Text.Json.JsonDocument.Parse(line);
                var root = doc.RootElement;
                string ts = root.TryGetProperty("ts", out var t) ? t.GetString() ?? "" : "";
                string level = root.TryGetProperty("level", out var l) ? l.GetString() ?? "" : "";
                string cid = root.TryGetProperty("cid", out var c) ? c.GetString() ?? "" : "";
                string msg = root.TryGetProperty("msg", out var m) ? m.GetString() ?? "" : "";
                return new ParsedLogLine(ts, level, cid, msg);
            }
            catch (System.Text.Json.JsonException)
            {
                return null;
            }
        }
    }

    public class TerminalLogger
    {
        public event EventHandler<LogMessageEventArgs>? OnLogMessage;
        private readonly List<LogMessageEventArgs> _history = new();
        private readonly object _lock = new();
        
        private readonly string _logsDirectory;
        private readonly Channel<LogMessageEventArgs> _logChannel;

        public TerminalLogger()
        {
            _logsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
            if (!Directory.Exists(_logsDirectory))
            {
                Directory.CreateDirectory(_logsDirectory);
            }

            _logChannel = Channel.CreateUnbounded<LogMessageEventArgs>(new UnboundedChannelOptions
            {
                SingleReader = true
            });
            Task.Run(ProcessLogQueueAsync);
        }

        private async Task ProcessLogQueueAsync()
        {
            await foreach (var e in _logChannel.Reader.ReadAllAsync())
            {
                try
                {
                    string dateStr = e.Timestamp.ToString("dd-MM-yyyy");
                    string fileName = $"{dateStr}-{e.Level}.log";
                    string fullPath = Path.Combine(_logsDirectory, fileName);
                    
                    // Remove Emojis (Surrogate pairs and common symbols)
                    string safeMsg = System.Text.RegularExpressions.Regex.Replace(e.Message, @"\p{Cs}|[✅❌🌐📈🔴🧪ℹ️🚨🏆💰⬇️⬆️🔹🔸✨🔥📊📝🔄⏸️]", "");
                    safeMsg = safeMsg.Replace("\r\n", " | ").Replace("\n", " | ").Replace("  ", " ").Trim();

                    string logLine = $"[{e.Timestamp:HH:mm:ss}] {safeMsg}{Environment.NewLine}";

                    await File.AppendAllTextAsync(fullPath, logLine);

                    // S-0: zusätzlich JSONL (eine Datei je Tag, alle Level) – maschinen-/KI-lesbar,
                    // Grundlage für den Log Viewer. Dual-Sink; Text-Sink später abschaltbar.
                    string jsonlPath = Path.Combine(_logsDirectory, $"{e.Timestamp:yyyy-MM-dd}.jsonl");
                    await File.AppendAllTextAsync(jsonlPath, LogJson.Format(e) + Environment.NewLine);
                }
                catch
                {
                    // Ignored to prevent cascading lockups
                }
            }
        }

        public void Log(string message, LogLevel level = LogLevel.Info, string correlationId = "")
        {
            var e = new LogMessageEventArgs(message, level, correlationId);
            lock (_lock)
            {
                _history.Add(e);
                // Optimize list pruning to avoid heavy O(N) operations per log
                if (_history.Count > 10500)
                {
                    // Remove older items efficiently in a batch
                    int itemsToRemove = _history.Count - 9000;
                    _history.RemoveRange(0, itemsToRemove);
                }
            }
            
            try 
            { 
                OnLogMessage?.Invoke(this, e); 
            }
            catch { }
            
            // Standard Console output as fallback/debug
            Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] [{level}] {message}");
            
            _logChannel.Writer.TryWrite(e);
        }

        public void Info(string message) => Log(message, LogLevel.Info);
        public void Debug(string message) => Log(message, LogLevel.Debug);
        public void Warning(string message) => Log(message, LogLevel.Warning);
        public void Error(string message) => Log(message, LogLevel.Error);
        public void Trade(string message) => Log(message, LogLevel.Trade);
        public void TradeReasoning(string message) => Log(message, LogLevel.TradeReasoning);
        
        public List<LogMessageEventArgs> GetHistory(TimeSpan maxAge)
        {
            lock (_lock)
            {
                var cutoff = DateTime.Now - maxAge;
                return _history.Where(x => x.Timestamp >= cutoff).ToList();
            }
        }
    }
}
