using Microsoft.AspNetCore.Components;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;

/// <summary>
/// A compact multi-select control: a Bootstrap dropdown whose toggle button
/// summarises the current selection ("All" when empty, the single item's label
/// when one is picked, or "N selected" otherwise) over a checkbox menu.
///
/// <para>
/// It exists to keep multi-value filter controls one row tall instead of a
/// wrapped block of chip buttons. The component mutates the caller-owned
/// <see cref="Selected"/> collection in place and raises
/// <see cref="SelectionChanged"/> after every toggle so the parent can react
/// (re-render, prune dependent selections, …).
/// </para>
///
/// <para>
/// Requires the Bootstrap JS bundle (loaded in <c>App.razor</c>) for the
/// dropdown toggle; <c>data-bs-auto-close="outside"</c> keeps the menu open
/// while the operator ticks several boxes.
/// </para>
/// </summary>
/// <typeparam name="TValue">The option value type (an enum or string).</typeparam>
public partial class MultiSelectDropdown<TValue> where TValue : notnull
{
    /// <summary>The options shown in the menu, in display order.</summary>
    [Parameter, EditorRequired]
    public IReadOnlyList<TValue> Items { get; set; } = Array.Empty<TValue>();

    /// <summary>
    /// The caller-owned selection set. Mutated in place by <see cref="Toggle"/>.
    /// </summary>
    [Parameter, EditorRequired]
    public ICollection<TValue> Selected { get; set; } = default!;

    /// <summary>Maps an option to its display label. Defaults to <c>ToString()</c>.</summary>
    [Parameter]
    public Func<TValue, string> Display { get; set; } = static v => v.ToString() ?? string.Empty;

    /// <summary>Raised after each toggle, once <see cref="Selected"/> has been updated.</summary>
    [Parameter]
    public EventCallback SelectionChanged { get; set; }

    /// <summary>Summary text shown on the toggle button when nothing is selected.</summary>
    [Parameter]
    public string AllLabel { get; set; } = "All";

    /// <summary>Text shown in the menu when there are no options.</summary>
    [Parameter]
    public string EmptyText { get; set; } = "None available";

    /// <summary><c>data-test</c> root for this control, its toggle and its options.</summary>
    [Parameter]
    public string DataTest { get; set; } = "multi-select";

    private async Task Toggle(TValue item)
    {
        // ICollection.Remove returns false when the item was absent — that is the
        // "not currently selected" case, so add it. This is a plain toggle.
        if (!Selected.Remove(item))
        {
            Selected.Add(item);
        }

        await SelectionChanged.InvokeAsync();
    }

    private string Summary()
    {
        var count = Selected.Count;
        if (count == 0)
        {
            return AllLabel;
        }

        if (count == 1)
        {
            // Prefer the single selection's label over a bare "1 selected".
            foreach (var item in Items)
            {
                if (Selected.Contains(item))
                {
                    return Display(item);
                }
            }

            // The one selected value is not in the current Items list (e.g. a Kind
            // narrowed out by a Channel change before the parent pruned it).
            return "1 selected";
        }

        return $"{count} selected";
    }
}
