using System.Linq.Expressions;

namespace IWS5.DatabaseAccess.Interfaces;

/// <summary>
/// Interface for a generic repository to perform CRUD operations.
/// </summary>
/// <typeparam name="T">The type of the entity.</typeparam>
public interface IIWSRepository<T> where T : class
{
    /// <summary>
    /// Checks if any entities match the specified predicate.
    /// </summary>
    /// <param name="predicate">The predicate to filter entities.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether any entities match the predicate.</returns>
    Task<bool> AnyAsync(Expression<Func<T, bool>> predicate);
    
    /// <summary>
    /// Lists entities based on the specified predicate and includes related entities.
    /// </summary>
    /// <param name="predicate">The predicate to filter entities. If null, all entities are returned.</param>
    /// <param name="includes">The related entities to include in the query.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns>
    Task<List<T>> ListAsync(Expression<Func<T, bool>>? predicate = null, params Expression<Func<T, object>>[] includes);

    /// <summary>
    /// Finds a single entity based on the specified predicate and includes.
    /// </summary>
    /// <param name="predicate">The predicate to filter entities.</param>
    /// <param name="includes">The related entities to include.</param>
    /// <returns>A task that represents the asynchronous operation. The task result contains the found entity.</returns>
    Task<T> FindAsync(Expression<Func<T, bool>>? predicate = null, params Expression<Func<T, object>>[] includes);

    /// <summary>
    /// Adds a new entity to the repository.
    /// </summary>
    /// <param name="entity">The entity to add.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task AddAsync(T entity);

    /// <summary>
    /// Adds a range of new entities to the repository.
    /// </summary>
    /// <param name="entities">The entities to add.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task AddRangeAsync(IEnumerable<T> entities);

    /// <summary>
    /// Removes an entity from the repository.
    /// </summary>
    /// <param name="entity">The entity to remove.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task RemoveAsync(T entity);

    /// <summary>
    /// Removes a range of entities from the repository.
    /// </summary>
    /// <param name="entities">The entities to remove.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task RemoveRangeAsync(IEnumerable<T> entities);

    /// <summary>
    /// Updates an existing entity in the repository.
    /// </summary>
    /// <param name="entity">The entity to update.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task UpdateAsync(T entity);

    /// <summary>
    /// Saves all changes made in the repository.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task SaveChangesAsync();

    /// <summary>
    /// Updates a range of entities from the repository.
    /// </summary>
    /// <param name="entities">The entities to update.</param>
    void UpdateRangeAsync(IEnumerable<T> entities);
}