@using MudBlazor
@using System.Collections
@using System.ComponentModel
@using System.Reflection
@using Inspectron.Settings
@using Inspectron.Settings.Attributes

<MudDialog>
    <DialogContent>
        <div class="settings-layout">
            <div class="settings-tree">
                <MudTreeView T="string" SelectedValueChanged="OnCategorySelected" Dense="true">
                    <SettingsTreeNode Nodes="_treeRoot" />
                </MudTreeView>
            </div>
            <div class="settings-content">
                @if (_selectedCategory != null && GetSettingsForCategory(_selectedCategory) is { Count: > 0 } settings)
                {
                    @foreach (var setting in settings)
                    {
                        <div class="settings-field">
                            @if (!string.IsNullOrEmpty(setting.SettingDescription))
                            {
                                <MudText Typo="Typo.caption" Class="mb-1" Style="font-style: italic;">@setting.SettingDescription</MudText>
                            }

                            @if (setting.Property.PropertyType == typeof(bool))
                            {
                                var boolVal = (bool)(setting.Value ?? false);
                                <MudSwitch T="bool" Value="boolVal"
                                           ValueChanged="@(v => setting.Value = v)"
                                           Label="@setting.Description" Color="Color.Primary" />
                            }
                            else if (setting.Property.PropertyType == typeof(int))
                            {
                                var intVal = (int)(setting.Value ?? 0);
                                <MudNumericField T="int" Value="intVal"
                                                 ValueChanged="@(v => setting.Value = v)"
                                                 Label="@setting.Description" Variant="Variant.Outlined" />
                            }
                            else if (setting.Property.PropertyType == typeof(double))
                            {
                                var dblVal = (double)(setting.Value ?? 0.0);
                                <MudNumericField T="double" Value="dblVal"
                                                 ValueChanged="@(v => setting.Value = v)"
                                                 Label="@setting.Description" Variant="Variant.Outlined" />
                            }
                            else if (setting.Property.PropertyType.IsEnum)
                            {
                                <MudSelect T="object" Value="setting.Value"
                                           ValueChanged="@(v => setting.Value = v)"
                                           Label="@setting.Description" Variant="Variant.Outlined">
                                    @foreach (var enumVal in Enum.GetValues(setting.Property.PropertyType))
                                    {
                                        <MudSelectItem T="object" Value="@enumVal">@enumVal.ToString()</MudSelectItem>
                                    }
                                </MudSelect>
                            }
                            else if (IsListType(setting.Property.PropertyType))
                            {
                                var s = setting;
                                <MudText Typo="Typo.subtitle2" Class="mb-1">@setting.Description</MudText>
                                <MudTextField T="string" Value="@GetListDisplayText(setting.Value as IList)"
                                              ReadOnly="true" Lines="3" Variant="Variant.Outlined" />
                                <MudButton Variant="Variant.Outlined" Color="Color.Primary" Class="mt-1"
                                           OnClick="@(() => EditList(s))">Edit</MudButton>
                            }
                            else
                            {
                                var strVal = setting.Value?.ToString() ?? "";
                                <MudTextField T="string" Value="strVal"
                                              ValueChanged="@(v => setting.Value = v)"
                                              Label="@setting.Description" Variant="Variant.Outlined" />
                            }
                        </div>
                    }
                }
                else
                {
                    <MudText Typo="Typo.body2" Class="pa-4">Select a category to view settings.</MudText>
                }
            </div>
        </div>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="Cancel">Cancel</MudButton>
        <MudButton Color="Color.Primary" OnClick="Save">Save</MudButton>
    </DialogActions>
</MudDialog>

@code {
    [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
    [Parameter] public InspectronSettings? Settings { get; set; }

    [Microsoft.AspNetCore.Components.Inject] private IDialogService DialogService { get; set; } = null!;

    private Dictionary<string, List<SettingEntry>> _categories = new();
    private string? _selectedCategory;
    private List<CategoryNode> _treeRoot = new();

    protected override void OnParametersSet()
    {
        if (Settings == null) return;
        LoadSettings();
    }

    private void LoadSettings()
    {
        _categories.Clear();
        _treeRoot = new List<CategoryNode>();
        var treeView = Settings!.UserSettings;
        var root = treeView.Root as Tree<object>;
        if (root != null)
            Traverse(root, "");

        foreach (var categoryPath in _categories.Keys)
        {
            var parts = categoryPath.Split('/');
            var currentList = _treeRoot;
            for (int i = 0; i < parts.Length; i++)
            {
                var existing = currentList.FirstOrDefault(n => n.Label == parts[i]);
                if (existing == null)
                {
                    existing = new CategoryNode
                    {
                        Label = parts[i],
                        FullPath = string.Join("/", parts.Take(i + 1))
                    };
                    currentList.Add(existing);
                }
                currentList = existing.ChildNodes;
            }
        }
    }

    private void Traverse(Tree<object> node, string path)
    {
        object value = node.Value;

        if (value is string folderName)
        {
            string newPath = string.IsNullOrEmpty(path) ? folderName : $"{path}/{folderName}";
            foreach (Tree<object> child in node.Children)
                Traverse(child, newPath);
        }
        else if (value is InspectronSettings.UserSettingsInfo info)
        {
            string groupName = info.Name;
            string groupPath = string.IsNullOrEmpty(path) ? groupName : $"{path}/{groupName}";
            foreach (PropertyDescriptor pd in info.Settings)
            {
                if (pd is BoundPropertyDescriptor bpd)
                {
                    var owner = bpd.Owner;
                    var description = string.IsNullOrEmpty(pd.Description) ? bpd.DisplayName : pd.Description;

                    string? settingDesc = null;
                    var descAttr = bpd.PropertyInfo.GetCustomAttribute<SettingDescriptionAttribute>();
                    if (descAttr != null)
                        settingDesc = descAttr.Description;

                    var entry = new SettingEntry
                    {
                        Owner = owner,
                        Property = bpd.PropertyInfo,
                        Description = description,
                        SettingDescription = settingDesc,
                        Value = bpd.PropertyInfo.GetValue(owner)
                    };

                    if (!_categories.TryGetValue(groupPath, out var list))
                    {
                        list = new List<SettingEntry>();
                        _categories[groupPath] = list;
                    }
                    list.Add(entry);
                }
            }
        }
    }

    private void OnCategorySelected(string? category)
    {
        _selectedCategory = category;
    }

    private List<SettingEntry> GetSettingsForCategory(string category)
    {
        if (_categories.TryGetValue(category, out var exact))
            return exact;

        var prefix = category + "/";
        return _categories
            .Where(kvp => kvp.Key.StartsWith(prefix))
            .SelectMany(kvp => kvp.Value)
            .ToList();
    }

    private static bool IsListType(Type type)
    {
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>);
    }

    private static string GetListDisplayText(IList? list)
    {
        if (list == null || list.Count == 0)
            return "(empty list)";
        var items = new List<string>();
        foreach (var item in list)
            items.Add(item?.ToString() ?? "(null)");
        return string.Join(Environment.NewLine, items);
    }

    private async Task EditList(SettingEntry setting)
    {
        var elementType = setting.Property.PropertyType.GetGenericArguments()[0];
        var parameters = new DialogParameters<ListEditorDialog>
        {
            { x => x.ElementType, elementType },
            { x => x.InitialList, setting.Value as IList }
        };
        var options = new DialogOptions { MaxWidth = MaxWidth.Medium, FullWidth = true };
        var dialog = await DialogService.ShowAsync<ListEditorDialog>($"Edit {setting.Description}", parameters, options);
        var result = await dialog.Result;
        if (result != null && !result.Canceled && result.Data is IList newList)
        {
            setting.Value = newList;
            StateHasChanged();
        }
    }

    private void Save()
    {
        foreach (var list in _categories.Values)
        {
            foreach (var setting in list)
            {
                var currentValue = setting.Property.GetValue(setting.Owner);
                if (!Equals(currentValue, setting.Value))
                {
                    try
                    {
                        if (IsListType(setting.Property.PropertyType) && setting.Value is IList listVal)
                        {
                            // For list types, clear and repopulate the original list
                            var originalList = setting.Property.GetValue(setting.Owner) as IList;
                            if (originalList != null)
                            {
                                originalList.Clear();
                                foreach (var item in listVal)
                                    originalList.Add(item);
                            }
                            else
                            {
                                setting.Property.SetValue(setting.Owner, setting.Value);
                            }
                        }
                        else if (setting.Value != null && setting.Value.GetType() != setting.Property.PropertyType)
                        {
                            var converted = Convert.ChangeType(setting.Value, setting.Property.PropertyType);
                            setting.Property.SetValue(setting.Owner, converted);
                        }
                        else
                        {
                            setting.Property.SetValue(setting.Owner, setting.Value);
                        }
                    }
                    catch
                    {
                    }
                }
            }
        }
        Settings?.SaveSettings();
        MudDialog.Close(DialogResult.Ok(true));
    }

    private void Cancel() => MudDialog.Cancel();

    private class SettingEntry
    {
        public object Owner { get; set; } = null!;
        public PropertyInfo Property { get; set; } = null!;
        public string Description { get; set; } = "";
        public string? SettingDescription { get; set; }
        public object? Value { get; set; }
    }

    public class CategoryNode
    {
        public string Label { get; set; } = "";
        public string FullPath { get; set; } = "";
        public List<CategoryNode> ChildNodes { get; set; } = new();
    }
}
