using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace PolyTrader.App.Avalonia.ViewModels
{
    /// <summary>Ein Eingabefeld der Einstellungen. Schreibt Änderungen direkt in das Zielobjekt.</summary>
    public abstract class SettingsField : INotifyPropertyChanged
    {
        protected readonly object Target;
        protected readonly PropertyInfo Property;

        protected SettingsField(object target, PropertyInfo property, string label, string? description)
        {
            Target = target;
            Property = property;
            Label = label;
            Description = description ?? string.Empty;
        }

        public string Label { get; }

        /// <summary>Erklärungstext aus dem <see cref="DescriptionAttribute"/> – erscheint klein unter dem Feld.</summary>
        public string Description { get; }

        public bool HasDescription => Description.Length > 0;

        /// <summary>Falsch bei Eigenschaften ohne Setter (reine Statusanzeigen).</summary>
        public bool IsEditable => Property.CanWrite;

        protected void Raise([CallerMemberName] string? name = null) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

        public event PropertyChangedEventHandler? PropertyChanged;
    }

    /// <summary>Freitext (string).</summary>
    public sealed class TextSettingsField : SettingsField
    {
        public TextSettingsField(object t, PropertyInfo p, string l, string? d) : base(t, p, l, d) { }

        public string Value
        {
            get => Property.GetValue(Target) as string ?? string.Empty;
            set { if (IsEditable) { Property.SetValue(Target, value ?? string.Empty); Raise(); } }
        }
    }

    /// <summary>Ja/Nein (bool) – als Kontrollkästchen.</summary>
    public sealed class BoolSettingsField : SettingsField
    {
        public BoolSettingsField(object t, PropertyInfo p, string l, string? d) : base(t, p, l, d) { }

        public bool Value
        {
            get => Property.GetValue(Target) is true;
            set { if (IsEditable) { Property.SetValue(Target, value); Raise(); } }
        }
    }

    /// <summary>
    /// Ganzzahl. Bewusst als Text mit invarianter Umwandlung: eine ungültige Eingabe lässt den
    /// bisherigen Wert stehen, statt still eine 0 zu schreiben.
    /// </summary>
    public sealed class IntSettingsField : SettingsField
    {
        public IntSettingsField(object t, PropertyInfo p, string l, string? d) : base(t, p, l, d) { }

        public string Value
        {
            get => Convert.ToInt32(Property.GetValue(Target) ?? 0).ToString(CultureInfo.InvariantCulture);
            set
            {
                if (!IsEditable) return;
                if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int parsed))
                    Property.SetValue(Target, parsed);
                Raise();
            }
        }
    }

    /// <summary>Auswahl aus festen Werten (Enum) – als Aufklappliste.</summary>
    public sealed class ChoiceSettingsField : SettingsField
    {
        public ChoiceSettingsField(object t, PropertyInfo p, string l, string? d) : base(t, p, l, d)
        {
            Options = Enum.GetNames(Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType);
        }

        public IReadOnlyList<string> Options { get; }

        public string? Value
        {
            get => Property.GetValue(Target)?.ToString();
            set
            {
                if (!IsEditable || value == null) return;
                Property.SetValue(Target, Enum.Parse(
                    Nullable.GetUnderlyingType(Property.PropertyType) ?? Property.PropertyType, value));
                Raise();
            }
        }
    }

    /// <summary>Nur-Lese-Anzeige (Eigenschaft ohne Setter, oder nicht editierbarer Typ).</summary>
    public sealed class ReadOnlySettingsField : SettingsField
    {
        public ReadOnlySettingsField(object t, PropertyInfo p, string l, string? d) : base(t, p, l, d) { }

        public string Value
        {
            get
            {
                object? raw = Property.GetValue(Target);
                if (raw is System.Collections.IEnumerable seq and not string)
                    return string.Join(", ", seq.Cast<object?>().Select(o => o?.ToString()));
                return raw?.ToString() ?? string.Empty;
            }
        }
    }

    /// <summary>Eine Kategorie mit ihren Feldern – entspricht einer Überschrift im Dialog.</summary>
    public sealed class SettingsSection
    {
        public string Title { get; init; } = string.Empty;
        public IReadOnlyList<SettingsField> Fields { get; init; } = Array.Empty<SettingsField>();
    }

    /// <summary>
    /// Baut aus einem Einstellungsobjekt die Abschnitte für den <c>SettingsEditor</c>.
    ///
    /// <para>Nutzt die Attribute, die für das frühere <c>PropertyGrid</c> ohnehin schon gepflegt
    /// wurden: <see cref="CategoryAttribute"/> gruppiert, <see cref="DisplayNameAttribute"/> liefert
    /// die Beschriftung, <see cref="DescriptionAttribute"/> den Hinweistext unter dem Feld, und
    /// <c>[Browsable(false)]</c> blendet aus (z.B. Watchdog-Token und Lizenzschlüssel – die gehören
    /// nicht offen in die Oberfläche).</para>
    ///
    /// <para>Damit ist der Ersatz des PropertyGrid keine Fleißarbeit je Feld: die Beschreibung der
    /// Felder steht weiterhin am Modell, nur die Darstellung ist jetzt eine eigene, gestaltbare
    /// Oberfläche statt einer generischen Reflection-Liste.</para>
    /// </summary>
    public static class SettingsModelBuilder
    {
        public static IReadOnlyList<SettingsSection> Build(object target)
        {
            var fields = target.GetType()
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.GetIndexParameters().Length == 0)
                .Where(p => p.GetCustomAttribute<BrowsableAttribute>()?.Browsable != false)
                .Select(p => new
                {
                    Property = p,
                    Category = p.GetCustomAttribute<CategoryAttribute>()?.Category ?? "Allgemein",
                    Label = p.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? p.Name,
                    Description = p.GetCustomAttribute<DescriptionAttribute>()?.Description
                })
                .ToList();

            return fields
                .GroupBy(f => f.Category)
                .Select(g => new SettingsSection
                {
                    Title = g.Key,
                    Fields = g.Select(f => Create(target, f.Property, f.Label, f.Description)).ToList()
                })
                .ToList();
        }

        private static SettingsField Create(object target, PropertyInfo p, string label, string? description)
        {
            Type t = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType;

            if (!p.CanWrite) return new ReadOnlySettingsField(target, p, label, description);
            if (t.IsEnum) return new ChoiceSettingsField(target, p, label, description);
            if (t == typeof(bool)) return new BoolSettingsField(target, p, label, description);
            if (t == typeof(int) || t == typeof(long)) return new IntSettingsField(target, p, label, description);
            if (t == typeof(string)) return new TextSettingsField(target, p, label, description);

            // Listen u.a. werden angezeigt, aber nicht hier bearbeitet (z.B. DisabledModules –
            // die pflegt der Modul-Tab des Dashboards).
            return new ReadOnlySettingsField(target, p, label, description);
        }
    }
}
