using Gridify;
using Gridify.EntityFramework;

namespace GerbilManagerWebAPI.Common
{
    /// <summary>Standard paged list envelope returned by every list endpoint.</summary>
    public record PagedResult<T>(IReadOnlyList<T> Items, int TotalCount, int Page, int PageSize);

    /// <summary>
    /// Query-string parameters for list endpoints. All optional (nullable) so the
    /// frontend can call a list with no params; Gridify's own GridifyQuery has
    /// non-nullable int Page/PageSize which [AsParameters] would make REQUIRED.
    /// Bound via [AsParameters]: ?filter=…&orderBy=…&page=1&pageSize=20.
    /// </summary>
    public class GridifyParams
    {
        public string? Filter { get; set; }
        public string? OrderBy { get; set; }
        public int? Page { get; set; }
        public int? PageSize { get; set; }

        public GridifyQuery ToQuery() => new()
        {
            Filter = NormalizeFilter(Filter),
            OrderBy = OrderBy,
            Page = Page is > 0 ? Page.Value : 1,
            PageSize = PageSize is > 0 ? PageSize.Value : 20,
        };

        // The frontend's gridify.ts emits "==" for equals (its documented convention),
        // but Gridify's equals operator is a single "=". Translate "==" -> "=". This is
        // safe because the frontend backslash-escapes any "=" inside values, and the
        // other operators it uses ( != >= <= =* ) contain no literal "==".
        private static string? NormalizeFilter(string? filter) =>
            string.IsNullOrEmpty(filter) ? filter : filter.Replace("==", "=");
    }

    public static class QueryableExtensions
    {
        /// <summary>
        /// Apply a Gridify query (filter/order/page) to an EF query and project each
        /// row to a DTO, returning the standard paged envelope. Filter/orderBy names
        /// are the ENTITY property names (case-insensitive), e.g. "status==Active",
        /// "orderBy=dateOfBirth", "litterId==…".
        /// </summary>
        public static async Task<PagedResult<TDto>> ToPagedResultAsync<TEntity, TDto>(
            this IQueryable<TEntity> source,
            GridifyParams parameters,
            Func<TEntity, TDto> map)
        {
            var query = parameters.ToQuery();
            Paging<TEntity> paging = await source.GridifyAsync(query);
            var items = paging.Data.Select(map).ToList();
            return new PagedResult<TDto>(items, paging.Count, query.Page, query.PageSize);
        }
    }
}
