using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;

namespace adas_core.Domain;

/// <summary>
///     Allow to pass to a <see cref="IQueryable" />
///     multiple requests like:
///     * Include (to fetch eagerly relations inside T entity)
///     Use a Builder pattern.
///     <example>
///         <code>
/// var queryCustomizer = QueryCustomizer&lt;Video&gt;.New()
///     .Include(v => v.Room)
///     .Include(v => v.Clips)
///     .Include(v => v.Diagnosis)
///     .Include(v => v.Movies)
///     .Include(v => v.Patient)
///     .Include(v => v.Service)
///     .Include(v => v.Thumbnail)
///     .Include(v => v.VideoDoctors)
///     .Include(v => v.Procedure)
///     .Include(v => v.Recordings)
///     .Include(v => v.Chapters)
///     .Include(v => v.LogDownloadVideoUsers)
///     .Include("VideoDoctors.Doctor")
///     ;
/// var v = _videoRepository.GetById(id, queryCustomizer);
/// </code>
///     </example>
/// </summary>
public class QueryCustomizer<T> where T : class
{
    private readonly List<Expression<Func<T, object>>> _includeLinqExpressions = [];
    private readonly List<string> _includeLinqStringExpressions = [];

    public static QueryCustomizer<T> New()
    {
        return new QueryCustomizer<T>();
    }

    public QueryCustomizer<T> Include(Expression<Func<T, object>> includeExpression)
    {
        _includeLinqExpressions.Add(includeExpression);
        return this;
    }

    public QueryCustomizer<T> Include(params Expression<Func<T, object>>[] includeExpressions)
    {
        _includeLinqExpressions.AddRange(includeExpressions);
        return this;
    }

    public QueryCustomizer<T> Include(string includeExpression)
    {
        _includeLinqStringExpressions.Add(includeExpression);
        return this;
    }

    public QueryCustomizer<T> Include(params string[] includeExpressions)
    {
        _includeLinqStringExpressions.AddRange(includeExpressions);
        return this;
    }


    public IQueryable<T> AddToQueryable(IQueryable<T> queryable)
    {
        var resultQueryable = queryable;
        resultQueryable = _includeLinqExpressions
                .Aggregate(resultQueryable, (current, expression) => current.Include(expression))
            ;
        resultQueryable = _includeLinqStringExpressions
                .Aggregate(resultQueryable, (current, expression) => current.Include(expression))
            ;
        return resultQueryable;
    }
}