using Contracts.BindingModels;
using Contracts.SearchModels;
using Contracts.StorageContracts;
using Contracts.ViewModels;
using DatabaseImplement.Models;
using DataModels.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DatabaseImplement.Implements
{
	public class PurchaseStorage : IPurchaseStorage
	{
		public PurchaseViewModel? Delete(PurchaseSearchModel model)
		{
            using var context = new Database();
            var element = context.Purchases.FirstOrDefault(rec => rec.Id == model.Id);
            if (element != null)
            {
                context.Purchases.Remove(element);
                context.SaveChanges();
                return element.GetViewModel;
            }
            return null;
        }

		public PurchaseViewModel? GetElement(PurchaseSearchModel model)
		{
            if (!model.Id.HasValue)
            {
                return null;
            }
            using var context = new Database();
            return context.Purchases
            .FirstOrDefault(x => (model.Id.HasValue && x.Id == model.Id))?.GetViewModel;
        }

		public List<PurchaseViewModel> GetFullList(PurchaseSearchModel? model)
		{
            using var context = new Database();
            return context.Purchases
            .ToList()
            .Select(x => x.GetViewModel)
            .ToList();
        }
        public List<PurchaseViewModel> GetFilteredList(PurchaseSearchModel? model)
        {
            using var context = new Database();
            if (model != null && model.UserId == Guid.Empty && !model.CostFrom.HasValue && !model.CostTo.HasValue && !model.DateTo.HasValue 
                && !model.DateFrom.HasValue && !model.Status.HasValue)
            {
                return new();
            }

            if (model.CostFrom.HasValue && model.CostTo.HasValue)
            {
                return context.Purchases
                .Where(x => x.Cost <= model.CostTo && x.Cost >= model.CostFrom)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.CostFrom.HasValue)
            {
                return context.Purchases
                .Where(x => x.Cost >= model.CostFrom)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.CostTo.HasValue)
            {
                return context.Purchases
                .Where(x => x.Cost <= model.CostTo)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.DateFrom.HasValue && model.DateTo.HasValue)
            {
                return context.Purchases
                .Where(x => x.DateCreated <= model.DateTo && x.DateCreated >= model.DateFrom)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.DateFrom.HasValue)
            {
                return context.Purchases
                .Where(x => x.DateCreated >= model.DateFrom)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.DateTo.HasValue)
            {
                return context.Purchases
                .Where(x => x.DateCreated <= model.DateTo)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            if (model.UserId != Guid.Empty)
            {
                return context.Purchases
                .Where(x => x.UserId == model.UserId)
                .ToList()
                .Select(x => x.GetViewModel)
                .ToList();
            }
            return context.Purchases
            .ToList()
            .Select(x => x.GetViewModel)
            .ToList();
        }     

        public PurchaseViewModel? Insert(PurchaseBindingModel model)
		{
            using var context = new Database();
            using var transaction = context.Database.BeginTransaction();
            try
            {
                var purchase = Purchase.Create(context, model);
                if (purchase == null)
                    return null;

                var cartItems = context.CartItems
                    .Where(x => !x.IsClosed).ToList();
                var updated = new List<CartItem>();

                foreach (var item in cartItems)
                {
                    var bitem = item.GetBindingModel();
                    bitem.IsClosed = true;
                    bitem.PurchaseId = purchase.Id;
                    bitem.Purchase = purchase;
                    item.Update(bitem);
                    updated.Add(item);
                    Console.WriteLine();
                }

                var products = new List<Product>();

                foreach (var item in updated)
                {
                    var product = context.Products
                        .FirstOrDefault(x => x.Id == item.ProductId);

                    products.Add(product);
                }

                purchase.Products = products;
                context.Purchases.Add(purchase);

                context.SaveChanges();
                transaction.Commit();

                return purchase.GetViewModel;
            }
            catch (DbUpdateException ex)
            {
                transaction.Rollback();
                throw new InvalidOperationException("Не удалось сохранить изменения.", ex);
            }
            finally
            {
                transaction.Dispose();
            }
            return null;

        }

        public PurchaseViewModel? Update(PurchaseBindingModel model)
        {
            using var context = new Database();
            using var transaction = context.Database.BeginTransaction();
            try
            {
                var purchase = context.Purchases.FirstOrDefault(rec =>
                rec.Id == model.Id);
                if (purchase == null)
                {
                    return null;
                }
                purchase.Update(model);
                context.SaveChanges();
                transaction.Commit();
                return purchase.GetViewModel;
            }
            catch
            {
                transaction.Rollback();
                throw;
            }
        }

        public List<CartItemViewModel> GetCartItems(PurchaseSearchModel model)
        {
            using var context = new Database();

            var cartItems = context.CartItems
                .Where(x => x.PurchaseId == model.Id).ToList()
                .Select(x => x.GetViewModel).ToList();

            if (cartItems == null)
            {
                return null;
            }

            return cartItems;
        }

        public List<ProductViewModel> GetProducts(PurchaseSearchModel model)
        {
            using var context = new Database();

            if (!model.Id.HasValue)
            {
                return null;
            }
            var products = new List<ProductViewModel>();
            var cartItems = GetCartItems(model);
            if (cartItems != null)
            {
                foreach (var item in cartItems)
                {
                    var product = context.Products
                        .FirstOrDefault(x => x.Id == item.ProductId)?.GetViewModel;
                    if (product != null) products.Add(product) ;
                }
            }
            else return null;

            return products;
        }
    }
}