using System;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection;
using PolyTrader.App.Avalonia.Shell;
using PolyTrader.Core;
using PolyTrader.Core.Modularity;
using PolyTraderSharp;
using PolyTraderSharp.Services;

namespace PolyTrader.App.Avalonia.Views
{
    /// <summary>
    /// Das einzige Anwendungsfenster. Hält die Seitenleiste, die Kopf-, Benachrichtigungs- und
    /// Statusleiste und den Inhaltsbereich, in dem die Seiten hängen.
    ///
    /// <para>Ersetzt die frühere Mehrfenster-Shell: statt je View ein <see cref="Window"/> mit
    /// eigener Menüleiste gibt es hier eine Navigation und einen Inhaltsbereich. Den Seitenwechsel
    /// steuert <see cref="AvaloniaUiHost"/>, das Fenster stellt dafür nur
    /// <see cref="ShowPage"/> bereit.</para>
    ///
    /// <para>Layout vollständig in ShellWindow.axaml.</para>
    /// </summary>
    public partial class ShellWindow : Window
    {
        /// <summary>Mehr als so viele Meldungen gleichzeitig würden den Inhalt zu weit nach unten drücken.</summary>
        private const int MaxNotifications = 3;

        private readonly AvaloniaUiHost _uiHost = null!;
        private readonly IServiceProvider _services = null!;
        private readonly TradingState _state = null!;
        private readonly ShellNavModel _nav = null!;
        private readonly DispatcherTimer _statusTimer = new() { Interval = TimeSpan.FromSeconds(1) };
        private readonly ObservableCollection<ShellNotification> _notifications = new();

        private TerminalLogger? _logger;
        private EventHandler<LogMessageEventArgs>? _logHandler;

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

        public ShellWindow(AvaloniaUiHost uiHost, IServiceProvider services) : this()
        {
            _uiHost = uiHost;
            _services = services;
            _state = services.GetRequiredService<TradingState>();

            _nav = new ShellNavModel(uiHost);
            DataContext = _nav;

            this.FindControl<ItemsControl>("notifications")!.ItemsSource = _notifications;
            _notifications.CollectionChanged += (_, _) =>
                this.FindControl<Border>("pnlNotifications")!.IsVisible = _notifications.Count > 0;

            this.FindControl<TextBlock>("lblVersion")!.Text = $"PolyTrader {BuildInfo.Version} · Avalonia";

            this.FindControl<Button>("btnCollapse")!.Click += (_, _) => _nav.ToggleSidebar();
            this.FindControl<Button>("btnQuit")!.Click += (_, _) => _uiHost.RequestShutdown();
            this.FindControl<Button>("btnTheme")!.Click += (_, _) => ThemeManager.Toggle();

            ThemeManager.ThemeChanged += UpdateThemeControls;
            UpdateThemeControls();

            SubscribeNotifications();

            _statusTimer.Tick += (_, _) => UpdateStatus();
            _statusTimer.Start();
            UpdateStatus();

            // Das X des Fensters beendet die Anwendung – deshalb über dieselbe Sicherheitsabfrage
            // wie „Beenden" (Verhalten der früheren LauncherForm).
            Closing += (_, e) =>
            {
                if (_uiHost.ShutdownConfirmed) return;
                e.Cancel = true;
                Dispatcher.UIThread.Post(_uiHost.RequestShutdown);
            };

            Closed += (_, _) =>
            {
                _statusTimer.Stop();
                ThemeManager.ThemeChanged -= UpdateThemeControls;
                if (_logger != null && _logHandler != null) _logger.OnLogMessage -= _logHandler;
                _nav.Detach();
            };
        }

        // ===== Seitenwechsel =====

        /// <summary>
        /// Hängt eine Seite in den Inhaltsbereich und setzt den Titel in der Kopfzeile.
        /// Wird ausschließlich von <see cref="AvaloniaUiHost.OpenView(ModuleView)"/> gerufen –
        /// dort liegt auch die Zwischenspeicherung der Seiten.
        /// </summary>
        public void ShowPage(string title, Control page)
        {
            _nav.PageTitle = title;
            this.FindControl<ContentControl>("pageHost")!.Content = page;
        }

        // ===== Farbschema =====

        private void UpdateThemeControls()
        {
            bool dark = ThemeManager.IsDark;

            // Sonne = ins Helle wechseln, Mond = ins Dunkle (Symbolik der bisherigen Menüleiste).
            var btn = this.FindControl<Button>("btnTheme")!;
            btn.Content = dark ? "☀" : "🌙";
            SetToolTip(btn, dark ? "Zum hellen Farbschema wechseln" : "Zum dunklen Farbschema wechseln");

            this.FindControl<TextBlock>("lblTheme")!.Text = dark ? "Dunkelmodus" : "Hellmodus";
        }

        private static void SetToolTip(Control control, string text) =>
            global::Avalonia.Controls.ToolTip.SetTip(control, text);

        // ===== Statusleiste =====

        private void UpdateStatus()
        {
            string trading = _state.GlobalTradingPaused
                ? "Pausiert"
                : $"Live={_state.LiveTradingMode} / Demo={_state.DemoTradingMode}";
            this.FindControl<TextBlock>("lblTrading")!.Text = $"Trading: {trading}";

            int moduleCount = _services.GetServices<IPolyTraderModule>().Count();
            this.FindControl<TextBlock>("lblModules")!.Text = $"Module: {moduleCount}";

            this.FindControl<TextBlock>("lblRatelimit")!.Text =
                $"API: {(_state.IsAlchemyHealthy ? "WSS aktiv" : "Polling")}";
        }

        // ===== Benachrichtigungsleiste =====

        /// <summary>
        /// Zeigt Warnungen und Fehler live an, sobald sie im Log auflaufen. Bewusst nur diese beiden
        /// Stufen: alles andere gehört in die Terminal-Seite und nicht über jede Seite.
        ///
        /// <para>Die wichtigsten Meldungen fallen beim <b>Start</b> an – fehlende Lizenz (dann laufen
        /// keine Module), fehlender Master-Key, keine Datenbank. Die entstehen alle, bevor dieses
        /// Fenster existiert, ein reines Abo würde sie also verpassen. Deshalb wird die Leiste
        /// zuerst aus dem jüngsten Verlauf gefüllt.</para>
        /// </summary>
        private void SubscribeNotifications()
        {
            _logger = _services.GetService<TerminalLogger>();
            if (_logger == null) return;

            foreach (var e in _logger.GetHistory(TimeSpan.FromMinutes(10))
                                     .Where(IsNoteworthy)
                                     .TakeLast(MaxNotifications))
            {
                Add(e);
            }

            _logHandler = (_, e) =>
            {
                if (!IsNoteworthy(e)) return;
                Dispatcher.UIThread.Post(() => Add(e));
            };
            _logger.OnLogMessage += _logHandler;
        }

        private static bool IsNoteworthy(LogMessageEventArgs e) =>
            e.Level == LogLevel.Warning || e.Level == LogLevel.Error;

        private void Add(LogMessageEventArgs e)
        {
            var note = new ShellNotification
            {
                Source = e.Timestamp.ToString("HH:mm:ss"),
                Message = OneLine(e.Message),
                IsError = e.Level == LogLevel.Error
            };
            note.Dismiss = new RelayCommand(() => _notifications.Remove(note));

            _notifications.Insert(0, note);
            while (_notifications.Count > MaxNotifications)
                _notifications.RemoveAt(_notifications.Count - 1);
        }

        private static string OneLine(string s) =>
            (s ?? string.Empty).Replace("\r", " ").Replace("\n", " ").Trim();
    }
}
