using System.Linq.Expressions;

namespace Jiaowu.Api.Infrastructure.Persistence;

public static class QueryableCollectionExtensions
{
    public static IQueryable<TEntity> WhereIn<TEntity, TValue>(
        this IQueryable<TEntity> source,
        IEnumerable<TValue> values,
        Expression<Func<TEntity, TValue>> valueSelector)
    {
        var predicate = BuildPredicate(values, valueSelector, Expression.OrElse, false);
        return source.Where(predicate);
    }

    public static IQueryable<TEntity> WhereNotIn<TEntity, TValue>(
        this IQueryable<TEntity> source,
        IEnumerable<TValue> values,
        Expression<Func<TEntity, TValue>> valueSelector)
    {
        var predicate = BuildPredicate(values, valueSelector, Expression.AndAlso, true);
        return source.Where(predicate);
    }

    private static Expression<Func<TEntity, bool>> BuildPredicate<TEntity, TValue>(
        IEnumerable<TValue> values,
        Expression<Func<TEntity, TValue>> valueSelector,
        Func<Expression, Expression, BinaryExpression> combine,
        bool negate)
    {
        Expression? body = null;
        foreach (var value in values.Distinct())
        {
            Expression comparison = Expression.Equal(
                valueSelector.Body,
                Expression.Constant(value, typeof(TValue)));
            if (negate)
                comparison = Expression.Not(comparison);
            body = body is null ? comparison : combine(body, comparison);
        }

        body ??= Expression.Constant(negate);
        return Expression.Lambda<Func<TEntity, bool>>(
            body,
            valueSelector.Parameters);
    }
}
