@namespace Lumeo
@typeparam TItem
@inject Lumeo.Services.Localization.ILumeoLocalizer L

<div class="@CssClass" @attributes="AdditionalAttributes">
    @if (Column?.FilterTemplate is not null)
    {
        <div class="p-3 space-y-3" data-slot="datagrid-filter-custom">
            @Column.FilterTemplate(new DataGridFilterTemplateContext(
                Column.Field,
                CurrentFilter,
                async desc => await OnApply.InvokeAsync(desc)))
        </div>
    }
    else
    {
    <div class="p-3 space-y-3">
        <div class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
            @L["Filter.FilterTitle", Column?.Title ?? ""]
        </div>

        @if (Column?.FilterType == DataGridFilterType.Boolean)
        {
            <label class="flex items-center gap-2 text-sm">
                <Checkbox Checked="@_boolValue" CheckedChanged="@OnBoolChanged" />
                <span>@(Column?.Title ?? "Value")</span>
            </label>
        }
        else
        {
            <Select Value="@_selectedOperator.ToString()" ValueChanged="@OnOperatorSelected">
                <SelectTrigger Class="h-8 w-full">
                    <span class="truncate text-sm">@GetOperatorLabel(_selectedOperator)</span>
                </SelectTrigger>
                <SelectContent>
                    @foreach (var op in GetAvailableOperators())
                    {
                        <SelectItem Value="@op.ToString()">@GetOperatorLabel(op)</SelectItem>
                    }
                </SelectContent>
            </Select>

            @if (Column?.FilterType == DataGridFilterType.Number)
            {
                <input type="number"
                       class="h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring"
                       placeholder="@L["Filter.ValuePlaceholder"]"
                       value="@_filterValue"
                       @oninput="OnValueInput" />
                @if (_selectedOperator == FilterOperator.Between)
                {
                    <input type="number"
                           class="h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring"
                           placeholder="@L["Filter.ToValuePlaceholder"]"
                           value="@_filterValueTo"
                           @oninput="OnValueToInput" />
                }
            }
            else if (Column?.FilterType == DataGridFilterType.Date)
            {
                <input type="date"
                       class="h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring"
                       value="@_filterValue"
                       @oninput="OnValueInput" />
                @if (_selectedOperator == FilterOperator.Between)
                {
                    <input type="date"
                           class="h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring"
                           value="@_filterValueTo"
                           @oninput="OnValueToInput" />
                }
            }
            else if (Column?.FilterType == DataGridFilterType.Select)
            {
                @if (FilterOptions is not null && FilterOptions.Count > 1)
                {
                    <div class="flex items-center justify-between mb-1">
                        <button type="button" class="text-xs text-primary hover:underline focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-2 rounded" @onclick="SelectAll">@L["Filter.SelectAll"]</button>
                        <button type="button" class="text-xs text-muted-foreground hover:underline focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-2 rounded" @onclick="ClearAll">@L["Filter.Clear"]</button>
                    </div>
                }
                <div class="max-h-40 overflow-auto space-y-1">
                    @if (FilterOptions is not null)
                    {
                        @foreach (var option in FilterOptions)
                        {
                            <label class="flex items-center gap-2 text-sm py-0.5">
                                <Checkbox Checked="@(_selectedOptions.Contains(option.Value?.ToString() ?? ""))"
                                          CheckedChanged="@(v => ToggleOption(option.Value?.ToString() ?? "", v))" />
                                <span>@option.Label</span>
                            </label>
                        }
                    }
                </div>
            }
            else
            {
                <input type="text"
                       class="h-8 w-full rounded-md border border-input bg-transparent px-3 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring"
                       placeholder="@L["Filter.ValuePlaceholder"]"
                       value="@_filterValue"
                       @oninput="OnValueInput" />
            }
        }

        <div class="flex items-center gap-2 pt-1">
            <button type="button"
                    class="flex-1 h-8 rounded-md bg-primary text-primary-foreground text-sm font-medium hover:bg-primary/90 transition-colors focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-2"
                    @onclick="Apply">
                @L["Filter.Apply"]
            </button>
            <button type="button"
                    class="flex-1 h-8 rounded-md border border-input bg-background text-sm hover:bg-accent hover:text-accent-foreground transition-colors focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring focus-visible:ring-offset-2"
                    @onclick="Clear">
                @L["Filter.Clear"]
            </button>
        </div>
    </div>
    }
</div>

@code {
    [Parameter] public DataGridColumn<TItem>? Column { get; set; }
    [Parameter] public List<FilterOption>? FilterOptions { get; set; }
    [Parameter] public FilterDescriptor? CurrentFilter { get; set; }
    [Parameter] public EventCallback<FilterDescriptor?> OnApply { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    // overflow-hidden is required so descendant hover/selected backgrounds
    // (e.g. inside a custom FilterTemplate) get clipped by the rounded-lg
    // corners — CSS border-radius alone only rounds the box itself, not its
    // children. min-w-64 (was w-64) lets a wider FilterTemplate grow the
    // popover instead of horizontally overflowing past the rounded edge.
    private string CssClass => $"min-w-64 rounded-lg border border-border/40 bg-popover text-popover-foreground shadow-md overflow-hidden {Class}".Trim();

    private FilterOperator _selectedOperator = FilterOperator.Contains;
    private string? _filterValue;
    private string? _filterValueTo;
    private bool _boolValue;
    private HashSet<string> _selectedOptions = new();

    protected override void OnParametersSet()
    {
        if (Column?.FilterType == DataGridFilterType.Number)
            _selectedOperator = FilterOperator.Equals;
        else if (Column?.FilterType == DataGridFilterType.Date)
            _selectedOperator = FilterOperator.Equals;
    }

    private void OnOperatorSelected(string value)
    {
        if (Enum.TryParse<FilterOperator>(value, out var op))
        {
            _selectedOperator = op;
        }
    }

    private void OnValueInput(ChangeEventArgs e)
    {
        _filterValue = e.Value?.ToString();
    }

    private void OnValueToInput(ChangeEventArgs e)
    {
        _filterValueTo = e.Value?.ToString();
    }

    private void OnBoolChanged(bool value)
    {
        _boolValue = value;
    }

    private void ToggleOption(string value, bool isChecked)
    {
        if (isChecked)
            _selectedOptions.Add(value);
        else
            _selectedOptions.Remove(value);
    }

    private void SelectAll()
    {
        if (FilterOptions is null) return;
        _selectedOptions = new HashSet<string>(FilterOptions.Select(o => o.Value?.ToString() ?? ""));
    }

    private void ClearAll()
    {
        _selectedOptions.Clear();
    }

    private async Task Apply()
    {
        if (Column?.Field is null) return;

        object? filterValue;
        if (Column.FilterType == DataGridFilterType.Boolean)
        {
            filterValue = _boolValue;
            _selectedOperator = FilterOperator.Equals;
        }
        else if (Column.FilterType == DataGridFilterType.Select)
        {
            filterValue = string.Join(",", _selectedOptions);
            _selectedOperator = FilterOperator.Contains;
        }
        else
        {
            filterValue = _filterValue;
        }

        var descriptor = new FilterDescriptor(
            Column.Field,
            _selectedOperator,
            filterValue,
            _filterValueTo,
            Column.FilterType
        );

        await OnApply.InvokeAsync(descriptor);
    }

    private async Task Clear()
    {
        _filterValue = null;
        _filterValueTo = null;
        _boolValue = false;
        _selectedOptions.Clear();
        await OnApply.InvokeAsync(null);
    }

    private List<FilterOperator> GetAvailableOperators()
    {
        var defaults = Column?.FilterType switch
        {
            DataGridFilterType.Number => new List<FilterOperator>
            {
                FilterOperator.Equals, FilterOperator.NotEquals,
                FilterOperator.GreaterThan, FilterOperator.GreaterThanOrEqual,
                FilterOperator.LessThan, FilterOperator.LessThanOrEqual,
                FilterOperator.Between, FilterOperator.IsEmpty, FilterOperator.IsNotEmpty
            },
            DataGridFilterType.Date => new List<FilterOperator>
            {
                FilterOperator.Equals, FilterOperator.NotEquals,
                FilterOperator.GreaterThan, FilterOperator.GreaterThanOrEqual,
                FilterOperator.LessThan, FilterOperator.LessThanOrEqual,
                FilterOperator.Between, FilterOperator.IsEmpty, FilterOperator.IsNotEmpty
            },
            DataGridFilterType.Select => new List<FilterOperator>
            {
                FilterOperator.Contains, FilterOperator.Equals
            },
            _ => new List<FilterOperator>
            {
                FilterOperator.Contains, FilterOperator.NotContains,
                FilterOperator.Equals, FilterOperator.NotEquals,
                FilterOperator.StartsWith, FilterOperator.EndsWith,
                FilterOperator.IsEmpty, FilterOperator.IsNotEmpty
            }
        };

        // If the column restricts operators, intersect with the default set so
        // unsupported combinations (e.g. StartsWith on a Number) aren't surfaced.
        if (Column?.Operators is { Count: > 0 } whitelist)
        {
            var allowed = new HashSet<FilterOperator>(whitelist);
            var filtered = defaults.Where(o => allowed.Contains(o)).ToList();
            // If the intersection is empty, fall back to the whitelist verbatim
            // (consumer clearly wants those operators even if we think they're odd).
            return filtered.Count > 0 ? filtered : whitelist;
        }

        return defaults;
    }

    private string GetOperatorLabel(FilterOperator op) => op switch
    {
        FilterOperator.Contains => L["Filter.Contains"],
        FilterOperator.NotContains => L["Filter.DoesNotContain"],
        FilterOperator.Equals => L["Filter.Equals"],
        FilterOperator.NotEquals => L["Filter.NotEquals"],
        FilterOperator.StartsWith => L["Filter.StartsWith"],
        FilterOperator.EndsWith => L["Filter.EndsWith"],
        FilterOperator.GreaterThan => L["Filter.GreaterThan"],
        FilterOperator.GreaterThanOrEqual => L["Filter.GreaterThanOrEqual"],
        FilterOperator.LessThan => L["Filter.LessThan"],
        FilterOperator.LessThanOrEqual => L["Filter.LessThanOrEqual"],
        FilterOperator.IsEmpty => L["Filter.IsEmpty"],
        FilterOperator.IsNotEmpty => L["Filter.IsNotEmpty"],
        FilterOperator.Between => L["Filter.Between"],
        _ => op.ToString()
    };
}
