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

namespace DiplomovaPrace.DataLayer.Queries.UserOrders
{
	[Service]
	public class OrderListQuery : QueryBase<OrderVM>, 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<OrderVM> Query()
		{
			Contract.Requires(Filter != null);
			Contract.Requires(Filter.UserID != 0);
			Contract.Requires(Sorting != null);

			IQueryable<Order> data = orderDataSource.Data
				.Where(i => i.OrderedDate != default && i.LoginAccountID == Filter.UserID)
				.WhereIf(Filter.OrderStateID != null, i => i.OrderStateID == Filter.OrderStateID);

			data = data.Include(i => i.DeliveryType)
				.Include(i => i.OrderItems)
				.Include(i => i.OrderState)
				.Include(i => i.PaymentType);

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

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

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

			return data.Select(i => new OrderVM
			{
				ID = i.ID,
				DeliveryType = i.DeliveryType.Name,
				OrderedDate = i.OrderedDate,
				OrderItemsCount = i.OrderItems.Count,
				OrderState = i.OrderState.Name,
				PaymentType = i.PaymentType.Name,
				TotalPrice = i.TotalPrice
			});
		}

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