using Sonex.Client.Dialogs;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Logger = Sonex.Client.Helpers.Logger;

namespace Sonex.Client.Controls.DataGridView
{
    // Odpowiedzialnosc pliku: model i wykonanie filtrow kolumnowych oraz szybkie filtrowanie.
    public sealed partial class DataGridView
    {
        public enum FilterComparison
        {
            Contains = 0,
            StartsWith = 1,
            EndsWith = 2,
            Equals = 3,
            NotEquals = 4,
            GreaterThan = 5,
            GreaterOrEqual = 6,
            LessThan = 7,
            LessOrEqual = 8,
            Between = 9,
            EqualsAny = 10,
            IsEmpty = 11,
            ContainsAny = 12,
            StartsWithAny = 13,
            EndsWithAny = 14,
            NotContainsAny = 15
        }

        internal enum BooleanFilterValue
        {
            True,
            False,
            Empty,
            NotEmpty
        }

        public sealed record FilterRule(string ColumnName, FilterComparison Comparison, object? Value, object? ValueTo = null);

        public string? Filter { get; private set; }

        private List<FilterRule> _filterRules = new();

        internal IReadOnlyList<Column> GetFilterColumns() => _Columns;

        internal IReadOnlyList<FilterRule> GetActiveFilterRules() => _filterRules;

        // Przyjmujemy reguły z UI i od razu odrzucamy te, które nie pasują do aktualnych kolumn.
        internal void ApplyColumnFiltersFromFlyout(IReadOnlyList<FilterRule> rules)
        {
            var knownColumns = new HashSet<string>(_Columns.Select(c => c.Name), StringComparer.OrdinalIgnoreCase);

            _filterRules = rules
                .Where(r => !string.IsNullOrWhiteSpace(r.ColumnName))
                .Where(r => knownColumns.Contains(r.ColumnName))
                .ToList();

            RefreshColumnFilterFlags();
            SetFilter(null);
        }

        internal static bool IsRangeComparableType(Type type)
        {
            return IsNumericType(type)
                || type == typeof(DateTime)
                || type == typeof(DateOnly)
                || type == typeof(TimeOnly)
                || type == typeof(TimeSpan)
                || type == typeof(char);
        }

        internal void RefreshColumnFilterFlags()
        {
            var filtered = new HashSet<string>(_filterRules.Select(r => r.ColumnName), StringComparer.OrdinalIgnoreCase);
            foreach (var column in _Columns)
            {
                column.IsFiltered = filtered.Contains(column.Name);
            }
        }

        private string? BuildColumnPredicate(out object[] args)
        {
            // W tym miejscu składamy finalny predykat typu: (warunek1) and (warunek2) and ...
            var parts = new List<string>();
            var values = new List<object>();

            foreach (var rule in _filterRules)
            {
                var column = _Columns.FirstOrDefault(c => c.Name.Equals(rule.ColumnName, StringComparison.OrdinalIgnoreCase));
                if (column == null)
                {
                    continue;
                }

                var clause = BuildColumnClause(column, rule, values);
                if (!string.IsNullOrWhiteSpace(clause))
                {
                    parts.Add($"({clause})");
                }
            }

            args = values.ToArray();
            if (parts.Count == 0)
            {
                return null;
            }

            return string.Join(" and ", parts);
        }

        private static string? BuildColumnClause(Column column, FilterRule rule, List<object> values)
        {
            // Każde porównanie mapujemy na bezpieczny fragment Dynamic LINQ + parametry @0, @1, ...
            var propertyName = column.PropertyInfo.Name;
            var propertyType = column.PropertyInfo.PropertyType;
            var nullableUnderlying = Nullable.GetUnderlyingType(propertyType);
            var effectiveType = nullableUnderlying ?? propertyType;
            var isNullableValueType = nullableUnderlying != null;
            var isReferenceType = !propertyType.IsValueType;

            switch (rule.Comparison)
            {
                case FilterComparison.Contains:
                case FilterComparison.StartsWith:
                case FilterComparison.EndsWith:
                    {
                        var text = Convert.ToString(rule.Value, CultureInfo.CurrentCulture)?.Trim();
                        if (string.IsNullOrWhiteSpace(text))
                        {
                            return null;
                        }

                        var idx = values.Count;
                        values.Add(text.ToLowerInvariant());

                        string accessor;
                        if (effectiveType == typeof(string))
                        {
                            accessor = $"{propertyName}.ToLower()";
                            if (isReferenceType)
                            {
                                accessor = $"{propertyName} != null && {accessor}";
                            }
                        }
                        else if (isNullableValueType)
                        {
                            accessor = $"{propertyName}.HasValue && {propertyName}.Value.ToString().ToLower()";
                        }
                        else if (isReferenceType)
                        {
                            accessor = $"{propertyName} != null && {propertyName}.ToString().ToLower()";
                        }
                        else
                        {
                            accessor = $"{propertyName}.ToString().ToLower()";
                        }

                        return rule.Comparison switch
                        {
                            FilterComparison.Contains => $"{accessor}.Contains(@{idx})",
                            FilterComparison.StartsWith => $"{accessor}.StartsWith(@{idx})",
                            FilterComparison.EndsWith => $"{accessor}.EndsWith(@{idx})",
                            _ => null
                        };
                    }

                case FilterComparison.ContainsAny:
                case FilterComparison.StartsWithAny:
                case FilterComparison.EndsWithAny:
                case FilterComparison.NotContainsAny:
                    {
                        var rawValues = EnumerateMultiValues(rule.Value);
                        if (rawValues.Count == 0)
                        {
                            return null;
                        }

                        string accessor;
                        if (effectiveType == typeof(string))
                        {
                            accessor = $"{propertyName}.ToLower()";
                            if (isReferenceType)
                            {
                                accessor = $"{propertyName} != null && {accessor}";
                            }
                        }
                        else if (isNullableValueType)
                        {
                            accessor = $"{propertyName}.HasValue && {propertyName}.Value.ToString().ToLower()";
                        }
                        else if (isReferenceType)
                        {
                            accessor = $"{propertyName} != null && {propertyName}.ToString().ToLower()";
                        }
                        else
                        {
                            accessor = $"{propertyName}.ToString().ToLower()";
                        }

                        var parts = new List<string>();
                        var uniqueNormalized = new HashSet<string>(StringComparer.Ordinal);

                        foreach (var raw in rawValues)
                        {
                            var text = Convert.ToString(raw, CultureInfo.CurrentCulture)?.Trim();
                            if (string.IsNullOrWhiteSpace(text))
                            {
                                continue;
                            }

                            var normalized = text.ToLowerInvariant();
                            if (!uniqueNormalized.Add(normalized))
                            {
                                continue;
                            }

                            var idx = values.Count;
                            values.Add(normalized);

                            var part = rule.Comparison switch
                            {
                                FilterComparison.ContainsAny => $"{accessor}.Contains(@{idx})",
                                FilterComparison.NotContainsAny => $"{accessor}.Contains(@{idx})",
                                FilterComparison.StartsWithAny => $"{accessor}.StartsWith(@{idx})",
                                FilterComparison.EndsWithAny => $"{accessor}.EndsWith(@{idx})",
                                _ => null
                            };

                            if (!string.IsNullOrWhiteSpace(part))
                            {
                                parts.Add(part);
                            }
                        }

                        if (parts.Count == 0)
                        {
                            return null;
                        }

                        if (parts.Count == 1)
                        {
                            return rule.Comparison == FilterComparison.NotContainsAny
                                ? $"!({parts[0]})"
                                : parts[0];
                        }

                        var combined = $"({string.Join(" or ", parts)})";
                        return rule.Comparison == FilterComparison.NotContainsAny
                            ? $"!({combined})"
                            : combined;
                    }

                case FilterComparison.Equals:
                case FilterComparison.NotEquals:
                    {
                        if (effectiveType == typeof(bool)
                            && TryBuildBooleanFilterClause(propertyName, rule.Value, isNullableValueType, isReferenceType, values, out var boolClause))
                        {
                            return boolClause;
                        }

                        if (effectiveType == typeof(DateTime)
                            && TryConvertRuleValueToDateTimeDayRange(rule.Value, propertyType, out var dayStart, out var nextDayStart))
                        {
                            var startIdx = values.Count;
                            values.Add(dayStart);

                            var endIdx = values.Count;
                            values.Add(nextDayStart);

                            if (rule.Comparison == FilterComparison.Equals)
                            {
                                if (isNullableValueType)
                                {
                                    return $"{propertyName}.HasValue && {propertyName}.Value >= @{startIdx} && {propertyName}.Value < @{endIdx}";
                                }

                                if (isReferenceType)
                                {
                                    return null;
                                }

                                return $"{propertyName} >= @{startIdx} && {propertyName} < @{endIdx}";
                            }

                            if (isNullableValueType)
                            {
                                return $"!{propertyName}.HasValue || {propertyName}.Value < @{startIdx} || {propertyName}.Value >= @{endIdx}";
                            }

                            if (isReferenceType)
                            {
                                return null;
                            }

                            return $"{propertyName} < @{startIdx} || {propertyName} >= @{endIdx}";
                        }

                        if (!TryConvertRuleValue(rule.Value, propertyType, out var converted))
                        {
                            return null;
                        }

                        var idx = values.Count;
                        values.Add(converted);

                        if (rule.Comparison == FilterComparison.Equals)
                        {
                            if (isNullableValueType)
                            {
                                return $"{propertyName}.HasValue && {propertyName}.Value == @{idx}";
                            }

                            if (isReferenceType)
                            {
                                return $"{propertyName} != null && {propertyName} == @{idx}";
                            }

                            return $"{propertyName} == @{idx}";
                        }

                        if (isNullableValueType)
                        {
                            return $"!{propertyName}.HasValue || {propertyName}.Value != @{idx}";
                        }

                        if (isReferenceType)
                        {
                            return $"{propertyName} == null || {propertyName} != @{idx}";
                        }

                        return $"{propertyName} != @{idx}";
                    }

                case FilterComparison.IsEmpty:
                    {
                        if (!TryConvertRuleValue(rule.Value, typeof(bool), out var convertedFlag)
                            || convertedFlag is not bool shouldBeEmpty)
                        {
                            return null;
                        }

                        if (effectiveType == typeof(string))
                        {
                            var emptyClause = $"{propertyName} == null || {propertyName} == \"\"";
                            return shouldBeEmpty ? $"({emptyClause})" : $"!({emptyClause})";
                        }

                        if (isNullableValueType)
                        {
                            var emptyClause = $"!{propertyName}.HasValue";
                            return shouldBeEmpty ? emptyClause : $"!({emptyClause})";
                        }

                        if (isReferenceType)
                        {
                            var emptyClause = $"{propertyName} == null";
                            return shouldBeEmpty ? emptyClause : $"!({emptyClause})";
                        }

                        // Non-nullable value types cannot be empty.
                        return shouldBeEmpty ? "false" : "true";
                    }

                case FilterComparison.EqualsAny:
                    {
                        var rawValues = EnumerateMultiValues(rule.Value);
                        if (rawValues.Count == 0)
                        {
                            return null;
                        }

                        if (effectiveType == typeof(DateTime))
                        {
                            var parts = new List<string>();
                            var uniqueDates = new HashSet<DateTime>();

                            foreach (var raw in rawValues)
                            {
                                if (!TryConvertRuleValue(raw, propertyType, out var converted))
                                {
                                    continue;
                                }

                                if (converted is not DateTime dateTime)
                                {
                                    continue;
                                }

                                var dayStart = dateTime.Date;
                                if (!uniqueDates.Add(dayStart))
                                {
                                    continue;
                                }

                                var nextDayStart = dayStart.AddDays(1);

                                var startIdx = values.Count;
                                values.Add(dayStart);

                                var endIdx = values.Count;
                                values.Add(nextDayStart);

                                if (isNullableValueType)
                                {
                                    parts.Add($"{propertyName}.HasValue && {propertyName}.Value >= @{startIdx} && {propertyName}.Value < @{endIdx}");
                                    continue;
                                }

                                if (isReferenceType)
                                {
                                    continue;
                                }

                                parts.Add($"{propertyName} >= @{startIdx} && {propertyName} < @{endIdx}");
                            }

                            if (parts.Count == 0)
                            {
                                return null;
                            }

                            if (parts.Count == 1)
                            {
                                return parts[0];
                            }

                            return $"({string.Join(" or ", parts)})";
                        }

                        var defaultParts = new List<string>();
                        var convertedUnique = new List<object>();

                        foreach (var raw in rawValues)
                        {
                            if (!TryConvertRuleValue(raw, propertyType, out var converted))
                            {
                                continue;
                            }

                            if (convertedUnique.Any(x => Equals(x, converted)))
                            {
                                continue;
                            }

                            convertedUnique.Add(converted);

                            var idx = values.Count;
                            values.Add(converted);

                            if (isNullableValueType)
                            {
                                defaultParts.Add($"{propertyName}.HasValue && {propertyName}.Value == @{idx}");
                                continue;
                            }

                            if (isReferenceType)
                            {
                                defaultParts.Add($"{propertyName} != null && {propertyName} == @{idx}");
                                continue;
                            }

                            defaultParts.Add($"{propertyName} == @{idx}");
                        }

                        if (defaultParts.Count == 0)
                        {
                            return null;
                        }

                        if (defaultParts.Count == 1)
                        {
                            return defaultParts[0];
                        }

                        return $"({string.Join(" or ", defaultParts)})";
                    }

                case FilterComparison.GreaterThan:
                case FilterComparison.GreaterOrEqual:
                case FilterComparison.LessThan:
                case FilterComparison.LessOrEqual:
                    {
                        if (!IsRangeComparableType(effectiveType))
                        {
                            return null;
                        }

                        if (effectiveType == typeof(DateTime)
                            && TryConvertRuleValueToDateTimeDayRange(rule.Value, propertyType, out var dayStart, out var nextDayStart))
                        {
                            object boundary = rule.Comparison switch
                            {
                                FilterComparison.GreaterThan => nextDayStart,
                                FilterComparison.GreaterOrEqual => dayStart,
                                FilterComparison.LessThan => dayStart,
                                FilterComparison.LessOrEqual => nextDayStart,
                                _ => dayStart
                            };

                            var idx = values.Count;
                            values.Add(boundary);

                            var op = rule.Comparison switch
                            {
                                FilterComparison.GreaterThan => ">=",
                                FilterComparison.GreaterOrEqual => ">=",
                                FilterComparison.LessThan => "<",
                                FilterComparison.LessOrEqual => "<",
                                _ => string.Empty
                            };

                            if (string.IsNullOrEmpty(op))
                            {
                                return null;
                            }

                            if (isNullableValueType)
                            {
                                return $"{propertyName}.HasValue && {propertyName}.Value {op} @{idx}";
                            }

                            if (isReferenceType)
                            {
                                return null;
                            }

                            return $"{propertyName} {op} @{idx}";
                        }

                        if (!TryConvertRuleValue(rule.Value, propertyType, out var converted))
                        {
                            return null;
                        }

                        var defaultIdx = values.Count;
                        values.Add(converted);

                        var defaultOp = rule.Comparison switch
                        {
                            FilterComparison.GreaterThan => ">",
                            FilterComparison.GreaterOrEqual => ">=",
                            FilterComparison.LessThan => "<",
                            FilterComparison.LessOrEqual => "<=",
                            _ => string.Empty
                        };

                        if (string.IsNullOrEmpty(defaultOp))
                        {
                            return null;
                        }

                        if (isNullableValueType)
                        {
                            return $"{propertyName}.HasValue && {propertyName}.Value {defaultOp} @{defaultIdx}";
                        }

                        if (isReferenceType)
                        {
                            return null;
                        }

                        return $"{propertyName} {defaultOp} @{defaultIdx}";
                    }

                case FilterComparison.Between:
                    {
                        if (!IsRangeComparableType(effectiveType))
                        {
                            return null;
                        }

                        if (effectiveType == typeof(DateTime))
                        {
                            if (!TryConvertRuleValue(rule.Value, propertyType, out var startRaw))
                            {
                                return null;
                            }

                            if (!TryConvertRuleValue(rule.ValueTo, propertyType, out var endRaw))
                            {
                                return null;
                            }

                            if (startRaw is not DateTime startDateTime || endRaw is not DateTime endDateTime)
                            {
                                return null;
                            }

                            var start = startDateTime.Date;
                            var endExclusive = endDateTime.Date.AddDays(1);

                            if (start >= endExclusive)
                            {
                                var normalizedStart = endDateTime.Date;
                                var normalizedEndExclusive = startDateTime.Date.AddDays(1);

                                start = normalizedStart;
                                endExclusive = normalizedEndExclusive;
                            }

                            var startIdx = values.Count;
                            values.Add(start);

                            var endIdx = values.Count;
                            values.Add(endExclusive);

                            if (isNullableValueType)
                            {
                                return $"{propertyName}.HasValue && {propertyName}.Value >= @{startIdx} && {propertyName}.Value < @{endIdx}";
                            }

                            if (isReferenceType)
                            {
                                return null;
                            }

                            return $"{propertyName} >= @{startIdx} && {propertyName} < @{endIdx}";
                        }

                        if (!TryConvertRuleValue(rule.Value, propertyType, out var startDefault))
                        {
                            return null;
                        }

                        if (!TryConvertRuleValue(rule.ValueTo, propertyType, out var endDefault))
                        {
                            return null;
                        }

                        NormalizeBetweenBounds(ref startDefault, ref endDefault);

                        var startDefaultIdx = values.Count;
                        values.Add(startDefault);

                        var endDefaultIdx = values.Count;
                        values.Add(endDefault);

                        if (isNullableValueType)
                        {
                            return $"{propertyName}.HasValue && {propertyName}.Value >= @{startDefaultIdx} && {propertyName}.Value <= @{endDefaultIdx}";
                        }

                        if (isReferenceType)
                        {
                            return null;
                        }

                        return $"{propertyName} >= @{startDefaultIdx} && {propertyName} <= @{endDefaultIdx}";
                    }

                default:
                    return null;
            }
        }

        private static bool TryBuildBooleanFilterClause(
            string propertyName,
            object? ruleValue,
            bool isNullableValueType,
            bool isReferenceType,
            List<object> values,
            out string? clause)
        {
            clause = null;
            if (ruleValue is not BooleanFilterValue booleanFilterValue)
            {
                return false;
            }

            switch (booleanFilterValue)
            {
                case BooleanFilterValue.True:
                    clause = BuildBooleanEqualsClause(propertyName, true, isNullableValueType, isReferenceType, values);
                    return true;

                case BooleanFilterValue.False:
                    clause = BuildBooleanEqualsClause(propertyName, false, isNullableValueType, isReferenceType, values);
                    return true;

                case BooleanFilterValue.Empty:
                    clause = isNullableValueType
                        ? $"!{propertyName}.HasValue"
                        : isReferenceType
                            ? $"{propertyName} == null"
                            : "false";
                    return true;

                case BooleanFilterValue.NotEmpty:
                    clause = isNullableValueType
                        ? $"{propertyName}.HasValue"
                        : isReferenceType
                            ? $"{propertyName} != null"
                            : "true";
                    return true;

                default:
                    return false;
            }
        }

        private static string BuildBooleanEqualsClause(
            string propertyName,
            bool value,
            bool isNullableValueType,
            bool isReferenceType,
            List<object> values)
        {
            var idx = values.Count;
            values.Add(value);

            if (isNullableValueType)
            {
                return $"{propertyName}.HasValue && {propertyName}.Value == @{idx}";
            }

            if (isReferenceType)
            {
                return $"{propertyName} != null && {propertyName} == @{idx}";
            }

            return $"{propertyName} == @{idx}";
        }

        private static List<object> EnumerateMultiValues(object? value)
        {
            var result = new List<object>();
            if (value == null)
            {
                return result;
            }

            if (value is string text)
            {
                var trimmed = text.Trim();
                if (!string.IsNullOrWhiteSpace(trimmed))
                {
                    result.Add(trimmed);
                }

                return result;
            }

            if (value is IEnumerable<string> strings)
            {
                foreach (var item in strings)
                {
                    var trimmed = item?.Trim();
                    if (!string.IsNullOrWhiteSpace(trimmed))
                    {
                        result.Add(trimmed);
                    }
                }

                return result;
            }

            if (value is System.Collections.IEnumerable enumerable)
            {
                foreach (var item in enumerable)
                {
                    if (item == null)
                    {
                        continue;
                    }

                    var textValue = Convert.ToString(item, CultureInfo.CurrentCulture)?.Trim();
                    if (!string.IsNullOrWhiteSpace(textValue))
                    {
                        result.Add(textValue);
                    }
                }

                return result;
            }

            result.Add(value);
            return result;
        }

        private static void NormalizeBetweenBounds(ref object start, ref object end)
        {
            if (start.GetType() != end.GetType())
            {
                return;
            }

            if (start is not IComparable comparable)
            {
                return;
            }

            if (comparable.CompareTo(end) <= 0)
            {
                return;
            }

            (start, end) = (end, start);
        }

        private static bool TryConvertRuleValueToDateTimeDayRange(
            object? value,
            Type propertyType,
            out DateTime startInclusive,
            out DateTime endExclusive)
        {
            startInclusive = default;
            endExclusive = default;

            if (!TryConvertRuleValue(value, propertyType, out var converted))
            {
                return false;
            }

            if (converted is not DateTime dateTime)
            {
                return false;
            }

            startInclusive = dateTime.Date;
            endExclusive = startInclusive.AddDays(1);
            return true;
        }

        private static bool TryConvertRuleValue(object? value, Type propertyType, out object converted)
        {
            converted = string.Empty;
            if (value == null)
            {
                return false;
            }

            var effectiveType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;

            if (effectiveType == typeof(string))
            {
                converted = Convert.ToString(value, CultureInfo.CurrentCulture) ?? string.Empty;
                return true;
            }

            if (effectiveType == typeof(bool))
            {
                if (value is bool boolValue)
                {
                    converted = boolValue;
                    return true;
                }

                if (value is string boolText && TryParseBool(boolText, out bool parsedBool))
                {
                    converted = parsedBool;
                    return true;
                }

                return false;
            }

            if (effectiveType.IsEnum)
            {
                if (value.GetType() == effectiveType)
                {
                    converted = value;
                    return true;
                }

                if (value is string enumText && Enum.TryParse(effectiveType, enumText, true, out object? enumValue) && enumValue != null)
                {
                    converted = enumValue;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(Guid))
            {
                if (value is Guid guid)
                {
                    converted = guid;
                    return true;
                }

                if (value is string guidText && Guid.TryParse(guidText, out var parsedGuid))
                {
                    converted = parsedGuid;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(DateTime))
            {
                if (value is DateTime dateTime)
                {
                    converted = dateTime;
                    return true;
                }

                if (value is DateOnly dateOnly)
                {
                    converted = dateOnly.ToDateTime(TimeOnly.MinValue);
                    return true;
                }

                if (value is string dateText && DateTime.TryParse(dateText, CultureInfo.CurrentCulture, DateTimeStyles.None, out var parsedDateTime))
                {
                    converted = parsedDateTime;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(DateOnly))
            {
                if (value is DateOnly dOnly)
                {
                    converted = dOnly;
                    return true;
                }

                if (value is DateTime dt)
                {
                    converted = DateOnly.FromDateTime(dt);
                    return true;
                }

                if (value is string dateOnlyText && DateOnly.TryParse(dateOnlyText, CultureInfo.CurrentCulture, DateTimeStyles.None, out var parsedDateOnly))
                {
                    converted = parsedDateOnly;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(TimeOnly))
            {
                if (value is TimeOnly tOnly)
                {
                    converted = tOnly;
                    return true;
                }

                if (value is TimeSpan span)
                {
                    converted = TimeOnly.FromTimeSpan(span);
                    return true;
                }

                if (value is string timeOnlyText && TimeOnly.TryParse(timeOnlyText, CultureInfo.CurrentCulture, DateTimeStyles.None, out var parsedTimeOnly))
                {
                    converted = parsedTimeOnly;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(TimeSpan))
            {
                if (value is TimeSpan ts)
                {
                    converted = ts;
                    return true;
                }

                if (value is TimeOnly to)
                {
                    converted = to.ToTimeSpan();
                    return true;
                }

                if (value is string tsText && TimeSpan.TryParse(tsText, CultureInfo.CurrentCulture, out var parsedTimeSpan))
                {
                    converted = parsedTimeSpan;
                    return true;
                }

                return false;
            }

            if (effectiveType == typeof(char))
            {
                if (value is char ch)
                {
                    converted = ch;
                    return true;
                }

                var charText = Convert.ToString(value, CultureInfo.CurrentCulture);
                if (!string.IsNullOrEmpty(charText) && charText.Length == 1)
                {
                    converted = charText[0];
                    return true;
                }

                return false;
            }

            if (TryParseNumber(value, effectiveType, out var number))
            {
                converted = number;
                return true;
            }

            return false;
        }

        private static bool TryParseNumber(object value, Type targetType, out object number)
        {
            number = 0;
            var culture = CultureInfo.CurrentCulture;
            var style = NumberStyles.Number;

            var text = Convert.ToString(value, culture);
            if (string.IsNullOrWhiteSpace(text))
            {
                return false;
            }

            if (targetType == typeof(byte) && byte.TryParse(text, style, culture, out var b))
            {
                number = b;
                return true;
            }

            if (targetType == typeof(sbyte) && sbyte.TryParse(text, style, culture, out var sb))
            {
                number = sb;
                return true;
            }

            if (targetType == typeof(short) && short.TryParse(text, style, culture, out var s))
            {
                number = s;
                return true;
            }

            if (targetType == typeof(ushort) && ushort.TryParse(text, style, culture, out var us))
            {
                number = us;
                return true;
            }

            if (targetType == typeof(int) && int.TryParse(text, style, culture, out var i))
            {
                number = i;
                return true;
            }

            if (targetType == typeof(uint) && uint.TryParse(text, style, culture, out var ui))
            {
                number = ui;
                return true;
            }

            if (targetType == typeof(long) && long.TryParse(text, style, culture, out var l))
            {
                number = l;
                return true;
            }

            if (targetType == typeof(ulong) && ulong.TryParse(text, style, culture, out var ul))
            {
                number = ul;
                return true;
            }

            if (targetType == typeof(float) && float.TryParse(text, style, culture, out var f))
            {
                number = f;
                return true;
            }

            if (targetType == typeof(double) && double.TryParse(text, style, culture, out var d))
            {
                number = d;
                return true;
            }

            if (targetType == typeof(decimal) && decimal.TryParse(text, style, culture, out var m))
            {
                number = m;
                return true;
            }

            return false;
        }

        private static bool TryParseBool(string input, out bool value)
        {
            switch (input.Trim().ToLowerInvariant())
            {
                case "1":
                case "true":
                case "yes":
                case "y":
                case "tak":
                case "t":
                    value = true;
                    return true;

                case "0":
                case "false":
                case "no":
                case "n":
                case "nie":
                    value = false;
                    return true;

                default:
                    value = false;
                    return false;
            }
        }

        public async void SetFilter(string? filter)
        {
            _ = filter;
            try
            {
                if (ItemsSource == null || ItemsSource.Count == 0 || ItemsType == null)
                {
                    _ItemsSourceFiltred = _ItemsSource;
                }
                else
                {
                    var predicate = BuildColumnPredicate(out object[] args);
                    Filter = predicate;

                    if (string.IsNullOrWhiteSpace(predicate))
                    {
                        _ItemsSourceFiltred = _ItemsSource;
                    }
                    else
                    {
                        _ItemsSourceFiltred = DynamicFilterRuntime.ApplyWhereToList(
                            items: ItemsSource,
                            itemType: ItemsType,
                            predicate: predicate,
                            args: args
                        );
                    }
                }
            }
            catch (Exception ex)
            {
                string errorId = Logger.Error(
                    ex,
                    source: nameof(DataGridView),
                    operation: nameof(SetFilter));
                await this.ShowMessageAsync("Error", Logger.BuildUserMessage(errorId));
            }

            ResetRowSelectionForCurrentView();
            NormalizeActiveCellForCurrentView();
            UpdateFooterTextSelected();
            _NeedCalcVerticalScroll = true;
            RequestRender();
            NotifyGridStateChanged("FilterApplied");
        }
    }
}