using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using PolyTrader.Core.Modularity;

namespace PolyTrader.App.Avalonia.Shell
{
    /// <summary>Schlanker Befehl ohne MVVM-Fremdbibliothek.</summary>
    public sealed class RelayCommand : ICommand
    {
        private readonly Action _execute;
        public RelayCommand(Action execute) => _execute = execute;
        public bool CanExecute(object? parameter) => true;
        public void Execute(object? parameter) => _execute();
        public event EventHandler? CanExecuteChanged { add { } remove { } }
    }

    /// <summary>
    /// Eine große Kachel auf der Startseite. Früher <c>WindowMenuEntry</c> – seit der Umstellung auf
    /// die Einfenster-Shell öffnet sie keine Fenster mehr, sondern schaltet die Seite um.
    /// </summary>
    public sealed class ViewTile
    {
        public string Title { get; init; } = "";
        public string? ToolTip { get; init; }
        public Bitmap? Icon { get; init; }

        /// <summary>Aktuell angezeigte Seite – wird fett dargestellt.</summary>
        public bool IsCurrent { get; init; }

        /// <summary>Seite wurde in dieser Sitzung schon aufgerufen – bekommt einen Haken.</summary>
        public bool IsChecked { get; init; }

        public ICommand Command { get; init; } = new RelayCommand(() => { });

        // Von den Bindungen im XAML genutzt (kein Konverter nötig).
        public string FontWeight => IsCurrent ? "Bold" : "Normal";
        public string CheckMark => IsChecked ? "✓ " : "";
    }

    /// <summary>Ein Eintrag der Seitenleiste.</summary>
    public sealed class ShellNavEntry
    {
        public string Title { get; init; } = "";
        public string? ToolTip { get; init; }
        public Bitmap? Icon { get; init; }

        /// <summary>Diese Seite wird gerade angezeigt – Hinterlegung im Akzent.</summary>
        public bool IsActive { get; init; }

        /// <summary>
        /// <c>false</c> für bekannte, aber nicht geladene Module: der Eintrag steht ausgegraut da
        /// und nennt im Hinweistext den Grund, statt einfach zu fehlen.
        /// </summary>
        public bool IsAvailable { get; init; } = true;

        /// <summary>
        /// Beschriftung anzeigen? Steht bewusst am Eintrag und nicht nur am Modell: so kommt die
        /// Vorlage mit einer geprüften Bindung auf ihren eigenen Datentyp aus und braucht keinen
        /// Umweg über den DataContext des Fensters (der wäre nur ungeprüft bindbar).
        /// </summary>
        public bool LabelsVisible { get; init; } = true;

        /// <summary>Eingeklappte Leiste: nur das Symbol, mittig gesetzt (Klassenbindung im XAML).</summary>
        public bool IconOnly => !LabelsVisible;

        public ICommand Command { get; init; } = new RelayCommand(() => { });
    }

    /// <summary>Ein Abschnitt der Seitenleiste („ÜBERSICHT", „MODULE", „SYSTEM").</summary>
    public sealed class ShellNavGroup
    {
        public string Title { get; init; } = "";
        public bool LabelsVisible { get; init; } = true;
        public IReadOnlyList<ShellNavEntry> Entries { get; init; } = Array.Empty<ShellNavEntry>();
    }

    /// <summary>
    /// Ein Eintrag der Benachrichtigungsleiste über dem Inhalt. Gespeist aus den Warnungen und
    /// Fehlern des <c>TerminalLogger</c> – dieselbe Quelle wie das Widget „Warnungen &amp; Fehler"
    /// auf der Startseite, nur live und wegklickbar.
    /// </summary>
    public sealed class ShellNotification
    {
        public string Source { get; init; } = "";
        public string Message { get; init; } = "";

        /// <summary>Fehler statt Warnung – färbt die Kante rot (Klassenbindung im XAML).</summary>
        public bool IsError { get; init; }

        /// <summary>
        /// Setzbar (nicht <c>init</c>), weil der Befehl den Eintrag selbst aus der Liste nimmt und
        /// ihn dafür kennen muss – er wird direkt nach dem Erzeugen zugewiesen.
        /// </summary>
        public ICommand Dismiss { get; set; } = new RelayCommand(() => { });
    }

    /// <summary>
    /// Datenmodell der Seitenleiste der Einfenster-Shell. Ersetzt das frühere
    /// <c>WindowMenuModel</c>, das auf JEDEM Fenster eine Menüleiste aufgespannt hat.
    ///
    /// <para><b>Layout-Regel:</b> Das Modell liefert nur Daten (<see cref="Groups"/>, Breite,
    /// Sichtbarkeit der Beschriftungen). Wie ein Eintrag aussieht, steht deklarativ in
    /// <c>Views/ShellWindow.axaml</c> – hier wird nichts zur Laufzeit zusammengebaut.</para>
    /// </summary>
    public sealed class ShellNavModel : INotifyPropertyChanged
    {
        /// <summary>Abschnitte der Seitenleiste in Anzeigereihenfolge.</summary>
        private const string GroupOverview = "ÜBERSICHT";
        private const string GroupModules = "MODULE";
        private const string GroupSystem = "SYSTEM";

        private static readonly string[] GroupOrder = { GroupOverview, GroupModules, GroupSystem };

        private readonly AvaloniaUiHost _host;
        private bool _expanded = true;
        private string _pageTitle = "";

        public ObservableCollection<ShellNavGroup> Groups { get; } = new();

        public ShellNavModel(AvaloniaUiHost host)
        {
            _host = host;
            Rebuild();
            _host.OpenStateChanged += OnOpenStateChanged;
        }

        /// <summary>Registrierung lösen, wenn die Shell geht – kein Event-Leak über die Host-Lebensdauer.</summary>
        public void Detach() => _host.OpenStateChanged -= OnOpenStateChanged;

        // ===== Seitenleiste ein-/ausklappen =====

        public bool IsExpanded => _expanded;

        /// <summary>Breite der Seitenleiste. Eingeklappt bleiben nur die Symbole stehen.</summary>
        public double SidebarWidth => _expanded ? 236 : 62;

        /// <summary>Beschriftungen (Marke, Abschnittstitel, Eintragstexte, Fußzeile) nur ausgeklappt.</summary>
        public bool LabelsVisible => _expanded;

        public string CollapseGlyph => _expanded ? "‹" : "›";
        public string CollapseToolTip => _expanded ? "Seitenleiste einklappen" : "Seitenleiste ausklappen";

        public void ToggleSidebar()
        {
            _expanded = !_expanded;
            Raise(nameof(IsExpanded));
            Raise(nameof(SidebarWidth));
            Raise(nameof(LabelsVisible));
            Raise(nameof(CollapseGlyph));
            Raise(nameof(CollapseToolTip));

            // Die Einträge tragen die Sichtbarkeit ihrer Beschriftung selbst (siehe ShellNavEntry) –
            // deshalb muss die Liste beim Ein-/Ausklappen neu aufgebaut werden.
            Rebuild();
        }

        // ===== Kopfzeile =====

        /// <summary>Titel der angezeigten Seite – steht groß in der Kopfzeile.</summary>
        public string PageTitle
        {
            get => _pageTitle;
            set { _pageTitle = value; Raise(nameof(PageTitle)); }
        }

        // ===== Navigation =====

        private void OnOpenStateChanged()
        {
            if (Dispatcher.UIThread.CheckAccess()) Rebuild();
            else Dispatcher.UIThread.Post(Rebuild);
        }

        private void Rebuild()
        {
            // Geladene Seiten und nicht geladene Module kommen aus zwei Quellen, sollen in der
            // Leiste aber gemeinsam nach Order stehen – deshalb erst zu einer Liste zusammenführen.
            var entries = _host.Views
                .Select(v => (
                    Group: GroupOf(v.Id),
                    v.Order,
                    v.Title,
                    Entry: new ShellNavEntry
                    {
                        Title = v.Title,
                        ToolTip = v.Title,
                        Icon = ViewIcons.Resolve(v.IconKey),
                        IsActive = v.Id == _host.ActiveViewId,
                        LabelsVisible = _expanded,
                        Command = new RelayCommand(() => _host.OpenView(v.Id))
                    }))
                .Concat(_host.UnavailableViews.Select(u => (
                    Group: GroupModules,
                    u.Order,
                    u.Title,
                    Entry: new ShellNavEntry
                    {
                        Title = u.Title,
                        ToolTip = u.Reason,
                        Icon = ViewIcons.Resolve(u.IconKey),
                        IsAvailable = false,
                        LabelsVisible = _expanded
                    })))
                .OrderBy(x => x.Order).ThenBy(x => x.Title)
                .GroupBy(x => x.Group)
                .ToDictionary(g => g.Key, g => g.Select(x => x.Entry).ToList());

            Groups.Clear();
            foreach (var groupTitle in GroupOrder)
            {
                if (!entries.TryGetValue(groupTitle, out var groupEntries)) continue;

                Groups.Add(new ShellNavGroup
                {
                    Title = groupTitle,
                    LabelsVisible = _expanded,
                    Entries = groupEntries
                });
            }

            Raise(nameof(Groups));
        }

        /// <summary>
        /// Ordnet eine View einem Abschnitt der Seitenleiste zu. Bewusst über die View-ID und nicht
        /// über <see cref="ModuleView.Group"/>: dort steht der Modulname („CopyTrading"), was in der
        /// Seitenleiste zu einem Abschnitt je Modul führen würde.
        /// </summary>
        private static string GroupOf(string viewId) => viewId switch
        {
            AvaloniaUiHost.StartViewId or "core.dashboard" => GroupOverview,
            "core.jobs" or "core.terminal" or "core.settings" => GroupSystem,
            _ => GroupModules
        };

        private void Raise(string name) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

        public event PropertyChangedEventHandler? PropertyChanged;
    }
}
