using System.Linq.Dynamic.Core;
using System.Linq.Expressions;

using Microsoft.EntityFrameworkCore;

using Monitoring.Interfaces;
using Monitoring.Models;

namespace Monitoring.Extensions;

public static class QueryableExtensions
{
	public static IQueryable<T> TryAddState<T>(this IQueryable<T> query, int? take, int? skip)
	{
		if (skip.HasValue)
		{
			query = query.Skip(skip.Value);
		}

		if (take.HasValue)
		{
			query = query.Take(take.Value);
		}

		return query;
	}

	public static Task<TableDataResponseModel<T>> GetTable<T>(
		this IQueryable<T> query, ITableStateContainer tableStateContainer, CancellationToken cancellationToken)
	{
		return query.GetTable(tableStateContainer.TableState, cancellationToken);
	}

	public static Task<TableDataResponseModel<T>> GetTable<T>(
		this IQueryable<T> query, ITableState tableState, CancellationToken cancellationToken)
	{
		return query.GetTable(tableState.Take, tableState.Skip, cancellationToken);
	}

	public static async Task<TableDataResponseModel<T>> GetTable<T>(
		this IQueryable<T> query, int? take, int? skip, CancellationToken cancellationToken)
	{
		var total = await query.CountAsync(cancellationToken);

		query = query.TryAddState(take, skip);

		var list = await query.ToListAsync(cancellationToken);

		return new(list, total);
	}

	public static IQueryable<T> ApplySort<T>(this IQueryable<T> query, ITableFilter filter)
	{
		if (filter.Sorts is not null && filter.Sorts.Any())
		{
			var sorts = filter.Sorts.Select(x => $"{x.Field} {x.Direction}").ToArray();
			query = query.OrderBy(string.Join(", ", sorts));
		}

		return query;
	}

	public static ValueTask<TableDataResponseModel<TModel>> GetTable<TModel, TEntity>(
		this IQueryable<TEntity> query,
		ITableState tableState,
		Expression<Func<TEntity, TModel>> mapping,
		CancellationToken cancellationToken)
	{
		return query.GetTable(tableState.Take, tableState.Skip, mapping, cancellationToken);
	}

	public static async ValueTask<TableDataResponseModel<TModel>> GetTable<TModel, TEntity>(
		this IQueryable<TEntity> query,
		int? take,
		int? skip,
		Expression<Func<TEntity, TModel>> mapping,
		CancellationToken cancellationToken)
	{
		var total = await query.CountAsync(cancellationToken);

		query = query.TryAddState(take, skip);
		var mappedQuery = query.Select(mapping);

		var list = await mappedQuery.ToListAsync(cancellationToken);

		return new(list, total);
	}
}
