<RadzenStack Orientation="Radzen.Orientation.Vertical" Gap=".5em">
    @if (string.IsNullOrWhiteSpace(Label) == false)
    {
        <RadzenLabel @bind-Text=Label Component="DropDownTextValueProperties" />
    }
    <RadzenTextBox class="j-input j-textbox" Value="@currentValue" Immediate="@Immediate" ValueChanged="@OnInnerValueChanged" Change="@OnInnerChange" />
</RadzenStack>

@code {
    [Parameter]
    public required string Label { get; set; }

    [Parameter]
    public string? Value { get; set; }

    [Parameter]
    public EventCallback<string?> ValueChanged { get; set; }

    [Parameter]
    public bool Immediate { get; set; }

    [Parameter]
    public int DebounceInterval { get; set; } = 300;

    /// <summary>If true, also emit immediately on blur/Enter (in addition to debounced typing).</summary>
    [Parameter]
    public bool EmitOnCommit { get; set; } = false;

    private string? currentValue;
    private CancellationTokenSource? cancellationTokenSource;

    protected override void OnParametersSet()
    {
        currentValue = Value;
    }

    private void OnInnerValueChanged(string? newValue)
    {
        currentValue = newValue;
        _ = DebounceEmitAsync();
        //Task.Run(DebounceEmitAsync);
    }

    private async Task DebounceEmitAsync()
    {
        cancellationTokenSource?.Cancel();
        cancellationTokenSource?.Dispose();
        cancellationTokenSource = new();

        try
        {
            await Task.Delay(DebounceInterval, cancellationTokenSource.Token);
            await ValueChanged.InvokeAsync(currentValue);
        }
        catch (TaskCanceledException)
        {
            
        }
    }

    private async Task OnInnerChange(object? _)
    {
        if (EmitOnCommit)
        {
            cancellationTokenSource?.Cancel();
            await ValueChanged.InvokeAsync(currentValue);
        }
    }

    public void Dispose()
    {
        cancellationTokenSource?.Cancel();
        cancellationTokenSource?.Dispose();
    }
}
