using System;
using System.Linq;
using System.Threading.Tasks;
using DiplomovaPrace.Contracts;
using DiplomovaPrace.Contracts.Orders;
using DiplomovaPrace.Contracts.Orders.ViewModels;
using DiplomovaPrace.DataLayer.DataSources.Orders;
using DiplomovaPrace.Model.Orders;
using Havit.Collections;
using Havit.Data.EntityFrameworkCore.Patterns.QueryServices;
using Havit.Diagnostics.Contracts;
using Havit.Extensions.DependencyInjection.Abstractions;
using Havit.Linq;

namespace DiplomovaPrace.DataLayer.Queries.Orders
{
	[Service]
	public class OrderListQuery : QueryBase<OrderListItem>, IOrderListQuery
	{
		private readonly IOrderDataSource orderDataSource;

		public OrderListQueryFilter Filter { get; set; }

		public SortItem[] Sorting { get; set; }

		public OrderListQuery(IOrderDataSource orderDataSource)
		{
			this.orderDataSource = orderDataSource;
		}

		protected override IQueryable<OrderListItem> Query()
		{
			Contract.Requires(Filter != null);
			Contract.Requires(Sorting != null);

			IQueryable<Order> data = orderDataSource.Data
				.Where(i => i.OrderedDate != default)
				.WhereIf(Filter.DeliveryTypeID != null, i => i.DeliveryTypeID == Filter.DeliveryTypeID)
				.WhereIf(Filter.OrderStateID != null, i => i.OrderStateID == Filter.OrderStateID)
				.WhereIf(Filter.PaymentStateID != null, i => i.PaymentStateID == Filter.PaymentStateID)
				.WhereIf(Filter.PaymentTypeID != null, i => i.PaymentTypeID == Filter.PaymentTypeID)
				.WhereIf(!string.IsNullOrEmpty(Filter.SearchString), i => i.ID.ToString().Contains(Filter.SearchString));

			// Possible future extension of sorting cases
			foreach (SortItem sortingItem in Sorting)
			{
				switch (sortingItem.Expression)
				{
					case nameof(OrderListItem.OrderedDate):
						data = data.OrderBy(i => i.OrderedDate, sortingItem.Direction);
						break;

					case nameof(OrderListItem.TotalPrice):
						data = data.OrderBy(i => i.TotalPrice, sortingItem.Direction);
						break;

					default:
						throw new InvalidOperationException($"Unknown SortingItem.Expression {sortingItem.Expression}.");
				}
			}

			return data.Select(i => new OrderListItem
			{
				ID = i.ID,
				DeliveryTypeID = i.DeliveryTypeID,
				OrderedDate = i.OrderedDate,
				OrderStateID = i.OrderStateID,
				PaymentStateID = i.PaymentStateID,
				PaymentTypeID = i.PaymentTypeID,
				TotalPrice = i.TotalPrice
			});
		}

		public async Task<PagedQueryResult<OrderListItem>> GetPageAsync(int pageIndex, int pageSize)
		{
			return new()
			{
				Data = await SelectPageAsync(pageIndex, pageSize).ConfigureAwait(false),
				TotalCount = await CountAsync().ConfigureAwait(false)
			};
		}
	}
}
