using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Threading;
using PolyTrader.App.Avalonia.ViewModels;
using PolyTrader.Core.Modularity;
using PolyTraderSharp.Services;

namespace PolyTrader.App.Avalonia.Views
{
    /// <summary>
    /// Terminal-/Log-Ansicht. Live-Ausgabe des <see cref="TerminalLogger"/> plus Viewer für die
    /// JSONL-Tagesdateien. Layout vollständig in TerminalWindow.axaml.
    /// </summary>
    public partial class TerminalWindow : UserControl
    {
        /// <summary>
        /// Obergrenze der angezeigten Zeilen. Die WinForms-Fassung leerte bei Erreichen einer
        /// Zeichengrenze das GESAMTE Terminal; hier werden stattdessen nur die ältesten Zeilen
        /// verdrängt, der jüngste Verlauf bleibt also immer sichtbar.
        /// </summary>
        private const int MaxLines = 5000;

        /// <summary>Höchstzahl je UI-Takt verarbeiteter Meldungen – hält die Oberfläche flüssig.</summary>
        private const int MaxPerTick = 500;

        private static readonly IBrush BrushDefault = Brushes.WhiteSmoke;
        private static readonly IBrush BrushError = Brushes.Tomato;
        private static readonly IBrush BrushWarning = Brushes.Gold;
        private static readonly IBrush BrushTrade = Brushes.LightGreen;
        private static readonly IBrush BrushReasoning = Brushes.Orange;

        private readonly ConcurrentQueue<LogMessageEventArgs> _queue = new();
        private readonly ObservableCollection<LogLine> _lines = new();
        private readonly ObservableCollection<LogViewerRow> _viewerRows = new();
        private readonly DispatcherTimer _timer = new() { Interval = TimeSpan.FromMilliseconds(250) };

        private TerminalLogger? _logger;
        private EventHandler<LogMessageEventArgs>? _logHandler;
        private bool _autoScroll = true;

        public TerminalWindow() => AvaloniaXamlLoader.Load(this);

        public TerminalWindow(IModuleUiHost host, TerminalLogger logger) : this()
        {
            _logger = logger;
            this.FindControl<ItemsControl>("logLines")!.ItemsSource = _lines;
            this.FindControl<DataGrid>("gridLogs")!.ItemsSource = _viewerRows;

            var levels = new[] { "Alle" }.Concat(Enum.GetNames<LogLevel>()).ToArray();
            var cbLevel = this.FindControl<ComboBox>("cbLogLevel")!;
            var cbViewerLevel = this.FindControl<ComboBox>("cbViewerLevel")!;
            cbLevel.ItemsSource = levels;
            cbViewerLevel.ItemsSource = levels;
            cbLevel.SelectedIndex = 0;
            cbViewerLevel.SelectedIndex = 0;

            this.FindControl<CalendarDatePicker>("dtViewerDate")!.SelectedDate = DateTime.Now;

            this.FindControl<Button>("btnAutoscroll")!.Click += (_, _) => ToggleAutoscroll();
            this.FindControl<Button>("btnClear")!.Click += (_, _) => { _lines.Clear(); UpdateLiveStatus(); };
            this.FindControl<Button>("btnCopyAll")!.Click += async (_, _) => await CopyAllAsync();
            this.FindControl<Button>("btnViewerLoad")!.Click += (_, _) => LoadViewer();

            // Doppelklick auf eine Zeile: nach deren CID filtern (komplette Signal-Kette ansehen).
            this.FindControl<DataGrid>("gridLogs")!.DoubleTapped += (_, _) =>
            {
                if (this.FindControl<DataGrid>("gridLogs")!.SelectedItem is LogViewerRow row
                    && !string.IsNullOrEmpty(row.Cid))
                {
                    this.FindControl<TextBox>("tbViewerCid")!.Text = row.Cid;
                    this.FindControl<TextBox>("tbViewerText")!.Text = string.Empty;
                    LoadViewer();
                }
            };

            // Jüngste Historie vorladen, damit das Fenster beim Öffnen nicht leer ist.
            foreach (var e in _logger.GetHistory(TimeSpan.FromMinutes(10)))
                _queue.Enqueue(e);

            _logHandler = (_, e) => _queue.Enqueue(e);
            _logger.OnLogMessage += _logHandler;

            _timer.Tick += (_, _) => ProcessQueue();
            _timer.Start();

            // Kein Abmelden mehr: Die Seite wird in der Einfenster-Shell einmal erzeugt und läuft
            // bis zum Programmende weiter – genau wie früher ein offen gelassenes Fenster. Der
            // Takt bleibt deshalb absichtlich an, damit der Verlauf beim Zurückwechseln vollständig
            // ist und keine Meldungen fehlen.

            ProcessQueue();
        }

        // ===== Live-Ausgabe =====

        private void ToggleAutoscroll()
        {
            _autoScroll = !_autoScroll;
            var btn = this.FindControl<Button>("btnAutoscroll")!;
            btn.Content = _autoScroll ? "Stop Autoscroll" : "Start Autoscroll";
            btn.Background = new SolidColorBrush(_autoScroll ? Colors.LightGreen : Colors.IndianRed);
        }

        private void ProcessQueue()
        {
            if (_queue.IsEmpty) return;

            string filter = this.FindControl<ComboBox>("cbLogLevel")!.SelectedItem as string ?? "Alle";
            int processed = 0;
            bool appended = false;

            while (processed < MaxPerTick && _queue.TryDequeue(out var e))
            {
                processed++;
                if (filter != "Alle" && e.Level.ToString() != filter) continue;

                // Anzeige-Zeitzone aus den Server-Settings (siehe AppTimeZone) – nicht mehr fest
                // gegen eine Windows-Zeitzonen-ID gerechnet.
                DateTime t = AppTimeZone.ToDisplay(e.Timestamp);

                _lines.Add(new LogLine
                {
                    Text = $"[{t:HH:mm:ss}] [{e.Level}] {e.Message}",
                    Brush = e.Level switch
                    {
                        LogLevel.Error => BrushError,
                        LogLevel.Warning => BrushWarning,
                        LogLevel.Trade => BrushTrade,
                        LogLevel.TradeReasoning => BrushReasoning,
                        _ => BrushDefault
                    }
                });
                appended = true;
            }

            // Ringpuffer: nur die aeltesten Zeilen verdraengen statt alles zu verwerfen.
            while (_lines.Count > MaxLines) _lines.RemoveAt(0);

            if (appended)
            {
                UpdateLiveStatus();
                if (_autoScroll) this.FindControl<ScrollViewer>("logScroll")!.ScrollToEnd();
            }
        }

        private void UpdateLiveStatus() =>
            this.FindControl<TextBlock>("lblLiveStatus")!.Text =
                $"{_lines.Count} Zeilen (max. {MaxLines}) · vollständige Logs im Ordner /Logs";

        private async System.Threading.Tasks.Task CopyAllAsync()
        {
            try
            {
                var sb = new StringBuilder();
                foreach (var l in _lines) sb.AppendLine(l.Text);

                // Seit der Umstellung auf Seiten ist „this" kein TopLevel mehr – die Zwischenablage
                // hängt am Shell-Fenster und wird über den Baum aufgelöst.
                var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
                if (clipboard != null) await clipboard.SetTextAsync(sb.ToString());
            }
            catch
            {
                // Zwischenablage kann kurzzeitig belegt sein – bewusst ignorieren, wie bisher.
            }
        }

        public async void OnContextMenuCopyClick(object? sender, RoutedEventArgs e) =>
            await CopyAllAsync();

        public async void OnContextMenuSelectAllClick(object? sender, RoutedEventArgs e) =>
            await CopyAllAsync();

        // ===== Log Viewer (JSONL-Tagesdateien) =====

        private void LoadViewer()
        {
            var status = this.FindControl<TextBlock>("lblViewerStatus")!;
            try
            {
                DateTime day = this.FindControl<CalendarDatePicker>("dtViewerDate")!.SelectedDate ?? DateTime.Now;
                string path = Path.Combine(AppContext.BaseDirectory, "Logs", $"{day:yyyy-MM-dd}.jsonl");

                _viewerRows.Clear();
                if (!File.Exists(path))
                {
                    status.Text = "Keine JSONL-Datei für dieses Datum.";
                    return;
                }

                string levelFilter = this.FindControl<ComboBox>("cbViewerLevel")!.SelectedItem as string ?? "Alle";
                string cidFilter = this.FindControl<TextBox>("tbViewerCid")!.Text?.Trim() ?? string.Empty;
                string textFilter = this.FindControl<TextBox>("tbViewerText")!.Text?.Trim() ?? string.Empty;

                int count = 0;
                foreach (string line in File.ReadLines(path))
                {
                    var p = LogJson.ParseLine(line);
                    if (p == null) continue;
                    if (levelFilter != "Alle" && p.Level != levelFilter) continue;
                    if (cidFilter.Length > 0 && !p.Cid.Contains(cidFilter, StringComparison.OrdinalIgnoreCase)) continue;
                    if (textFilter.Length > 0 && !p.Message.Contains(textFilter, StringComparison.OrdinalIgnoreCase)) continue;

                    _viewerRows.Add(new LogViewerRow { Time = p.Time, Level = p.Level, Cid = p.Cid, Message = p.Message });
                    if (++count >= 20000) break;   // Schutz bei sehr grossen Tagen
                }

                status.Text = $"{_viewerRows.Count} Einträge.";
            }
            catch (Exception ex)
            {
                status.Text = $"Fehler: {ex.Message}";
            }
        }
    }
}
