@*
 *                                dbMango
 *
 * Copyright 2025 Deutsche Bank AG
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *@
<div class="dropdown @Class">
  <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
        @if (ShowSelectionText)
        {
            var s = string.Join(", ", _selectedValues.OrderBy(x => x).Select(x => string.IsNullOrWhiteSpace(x) ? DenoteEmptyString : x));
            if ( s == string.Empty )
            {
                s = "(None)";
            }
            else if ( s.Length > 30 )
            {
                s = s[..20] + "...";
            }
            <span>@s</span>
        }
        else
        {
            @Name
        }
  </button>
    <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
        @if (EnableDropdownMenu)
        {
            <div class="dropdown-item">
                <span>
                    <button class="btn" @onclick="ToggleFilterable" title="toggle type-in filter">
                        <span class="m-auto ui-icon-font ui-icon-sm icon-search-sm"></span>
                    </button></span>
                <span><button class="btn" @onclick="@CancelClicked" id="cancel" name="cancel" title="discard changes">Cancel</button></span>
                <span><button class="btn" @onclick="@ApplyClicked" id="apply" name="apply" title="apply changes">Apply</button></span>
            </div>
        }
        @if (Filterable)
        {
            <div class="dropdown-item">
                <input class="form-control" @bind="FilterString" @bind:event="oninput"/>
            </div>
        }
        <div class="dropdown-item">
            @if (_allSelected)
            {
                <input type="checkbox" @onchange="_ => AllClicked()" checked id="all" name="all" value="all">
            }
            else
            {
                <input type="checkbox" @onchange="_ => AllClicked()" id="all" name="all" value="all">
            }
            <label for="all">(All)</label>
        </div>
        @foreach (var item in _filteredDropdown)
        {
            <div class="dropdown-item">
                @if (_selectedValues.Contains(item))
                {
                    <input type="checkbox" @onchange="eventArgs => { ItemCheckboxClicked(item, eventArgs.Value); }" checked id=@item name=@item value=@item>
                }
                else
                {
                    <input type="checkbox" @onchange="eventArgs => { ItemCheckboxClicked(item, eventArgs.Value); }" id=@item name=@item value=@item>
                }
                <label for=@item>@item</label>
            </div>
        }
    </div>
</div>

@code
{
    [Parameter] public string Name              { get; set; } = "Select";
    [Parameter] public string DenoteEmptyString { get; set; } = "(Blank)";
    [Parameter] public string Class             { get; set; } = "";
    [Parameter] public bool   ShowSelectionText { get; set; }

    [Parameter]
    public List<string> DropdownValues
    {
        get;
        set
        {
            value ??= [];
            if (field.SequenceEqual(value))
                return;

            field = value;
            SetValues(BlankedDropdownValues);
            UpdateAllSelected();
            InvokeAsync(StateHasChanged);
        }
    } = [];

    [Parameter] public EventCallback<List<string>> SelectionChanged { get; set; }
    [Parameter] public bool EnableDropdownRefresh                   { get; set; } 
    [Parameter] public bool EnableDropdownMenu                      { get; set; } = true;
    [Parameter] public bool Filterable                              { get; set; } 
    [Parameter] public bool ResetNow                                { get; set; } 
    [Parameter] public bool SelectAllByDefault                      { get; set; } = true;

    [Parameter]
    public string FilterString
    {
        get;
        set
        {
            field       = value;
            _filterList = field.Split(",").Select(v => v.Trim()).Where(v => !string.IsNullOrWhiteSpace(v)).ToList();
            RefreshFilter();
        }
    } = string.Empty;

    private bool            _allSelected          = true;
    private List<string>    _dropdownValuesList   = [];
    private HashSet<string> _dropdownValuesSet    = [];
    private HashSet<string> _selectedValues       = [];
    private HashSet<string> _selectedValuesBackup = [];
    private List<string>    _filterList           = [];
    private List<string>    _filteredDropdown     = [];

    IEnumerable<string> BlankedDropdownValues
        => DropdownValues?.Select(x => string.IsNullOrWhiteSpace(x) ? DenoteEmptyString : x) ?? Array.Empty<string>();

    [Parameter] public HashSet<string> SelectedValues
    {
        get => _selectedValues;
        set
        {
            if (value == null || ReferenceEquals(_selectedValues, value) || _selectedValues.SequenceEqual(value))
                return;
            _selectedValues = value;
            SelectedValuesChanged.InvokeAsync(_selectedValues);
        }
    }

    [Parameter] public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }


    private void RefreshFilter()
    {
        _filteredDropdown.Clear();
        if (_filterList.Count == 0)
            _filteredDropdown.AddRange(BlankedDropdownValues);
        else
            _filteredDropdown.AddRange(
                BlankedDropdownValues.Where(x =>
                    _filterList.Any(v => x.Contains(v, StringComparison.OrdinalIgnoreCase))));
    }

    private void SetValues(IEnumerable<string> values)
    {
        _dropdownValuesList.Clear();
        _dropdownValuesList.AddRange(values);
        _dropdownValuesSet.Clear();
        _dropdownValuesSet.UnionWith(_dropdownValuesList);
    }

    private void ResetMenu(IEnumerable<string> values)
    {
        SetValues(values);
        _selectedValues    .Clear();
        _selectedValues    .UnionWith(_dropdownValuesSet);
        _allSelected = true;

        SelectedValuesChanged.InvokeAsync(_selectedValues);
    }

    protected override void OnInitialized()
    {
        if (SelectAllByDefault)
            ResetMenu(BlankedDropdownValues);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (!firstRender)
            return;

        if (!SelectAllByDefault)
        {
            SetValues(BlankedDropdownValues);

            if ( UpdateAllSelected() )
                await InvokeAsync(StateHasChanged);
        }
    }

    private bool UpdateAllSelected()
    {
        var sel = _selectedValues.SetEquals(_dropdownValuesSet);
        if (_allSelected != sel)
        {
            _allSelected = sel;
            return true;
        }
        return false;
    }

    protected override void OnParametersSet()
    {
        if (ResetNow)
        {
            ResetMenu(BlankedDropdownValues);
            FilterString = string.Empty;
        }
        else if (EnableDropdownRefresh)
        {
            var newDropdownValues = new HashSet<string>(BlankedDropdownValues);
            if (!newDropdownValues.SetEquals(_dropdownValuesSet))
                ResetMenu(BlankedDropdownValues);
        }
        RefreshFilter();
    }

    public async Task AllClicked()
    {
        _allSelected = !_allSelected;
        _selectedValues.Clear();
        if (_allSelected)
            _selectedValues.UnionWith(_dropdownValuesSet);

        await SelectedValuesChanged.InvokeAsync(_selectedValues);
        await InvokeAsync(StateHasChanged);
    }

    public void ItemCheckboxClicked(string item, object? checkedValue)
    {
        if (checkedValue is true)
            _selectedValues.Add(item);
        else
            _selectedValues.Remove(item);
        _allSelected = _selectedValues.SetEquals(_dropdownValuesSet);

        SelectedValuesChanged.InvokeAsync(_selectedValues);
    }

    public void CancelClicked()
    {
        _selectedValues.Clear();
        _selectedValues.UnionWith(_selectedValuesBackup);
        _allSelected = _selectedValues.SetEquals(_dropdownValuesSet);

        SelectedValuesChanged.InvokeAsync(_selectedValues);
    }

    public Task ApplyClicked()
    {
        _selectedValuesBackup.Clear();
        _selectedValuesBackup.UnionWith(_selectedValues);
        return SelectionChanged.InvokeAsync(_selectedValues.Select(x => x == DenoteEmptyString ? "" : x)
                                                           .ToList());
    }

    private void ToggleFilterable()
    {
    
        Filterable = !Filterable;
        FilterString = string.Empty;
    }
}
