using DiplomovaPrace.Contracts.Customer.Addresses;
using DiplomovaPrace.Contracts.Customer.Addresses.InputModels;
using DiplomovaPrace.DataLayer.Repositories.Orders;
using DiplomovaPrace.DataLayer.Repositories.Security;
using DiplomovaPrace.Model.Orders;
using DiplomovaPrace.Model.Security;
using Havit.Data.Patterns.DataLoaders;
using Havit.Data.Patterns.UnitOfWorks;
using Havit.Extensions.DependencyInjection.Abstractions;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DiplomovaPrace.Facades.Customer.Addresses
{
	[Service]
	public class AddressFacade : IAddressFacade
	{
		private readonly IDataLoader dataLoader;
		private readonly IUnitOfWork unitOfWork;
		private readonly IAddressRepository addressRepository;
		private readonly IAddressTypeRepository addressTypeRepository;
		private readonly ILoginAccountRepository loginAccountRepository;
		private readonly IOrderRepository orderRepository;

		public AddressFacade(IDataLoader dataLoader,
		IUnitOfWork unitOfWork,
		IAddressRepository addressRepository,
		IAddressTypeRepository addressTypeRepository,
		ILoginAccountRepository loginAccountRepository,
		IOrderRepository orderRepository)
		{
			this.dataLoader = dataLoader;
			this.unitOfWork = unitOfWork;
			this.addressRepository = addressRepository;
			this.addressTypeRepository = addressTypeRepository;
			this.loginAccountRepository = loginAccountRepository;
			this.orderRepository = orderRepository;
		}

		public async Task<UserAddressDto> CreateUserAddressAsync(int userID, CreateAddressIM inputModel)
		{
			LoginAccount loginAccount = await loginAccountRepository.GetObjectAsync(userID);
			Model.Orders.AddressType addressType = await addressTypeRepository.GetObjectAsync(inputModel.AddressTypeID);

			if (loginAccount is not null && addressType is not null)
			{
				Address address = new()
				{
					FirstName = inputModel.FirstName,
					LastName = inputModel.LastName,
					PhoneNumber = inputModel.PhoneNumber,
					City = inputModel.City,
					Street = inputModel.Street,
					ZipCode = inputModel.ZipCode,
					State = "Česká republika",
					AddressTypeID = addressType.ID,
					LoginAccountID = loginAccount.ID
				};

				unitOfWork.AddForInsert(address);
				await unitOfWork.CommitAsync();

				return new UserAddressDto()
				{
					ID = address.ID,
					City = address.City,
					FirstName = address.FirstName,
					LastName = address.LastName,
					PhoneNumber = address.PhoneNumber,
					Street = address.Street,
					ZipCode = address.ZipCode,
					AddressTypeID = address.AddressTypeID
				};
			}

			return null;
		}

		public async Task<bool> DeleteUserAddressAsync(int addressID, int userID)
		{
			List<Address> userAddresses = await addressRepository.GetActiveAddressesByUserIDAsync(userID);

			if (userAddresses.Count > 0)
			{
				Address addressToDelete = userAddresses.FirstOrDefault(i => i.ID == addressID);
				
				if (addressToDelete is not null)
				{
					unitOfWork.AddForDelete(addressToDelete);
					await unitOfWork.CommitAsync();

					return true;
				}
			}

			return false;
		}

		public async Task<List<UserAddressDto>> GetUserAddressesAsync(int userID)
		{
			List<Address> addresses = await addressRepository.GetActiveAddressesByUserIDAsync(userID);
			await dataLoader.LoadAllAsync(addresses, i => i.LoginAccount);

			return addresses.Select(i => new UserAddressDto
			{
				ID = i.ID,
				City = i.City,
				Email = i.LoginAccount.Email,
				FirstName = i.FirstName,
				LastName = i.LastName,
				PhoneNumber = i.PhoneNumber,
				Street = i.Street,
				ZipCode = i.ZipCode,
				AddressTypeID = i.AddressTypeID
			}).OrderBy(i => i.AddressTypeID).ToList();
		}

		public async Task<UserAddressDto> UpdateUserAddressAsync(int userID, UpdateAddressIM inputModel)
		{
			List<Address> userAddresses = await addressRepository.GetActiveAddressesByUserIDAsync(userID);

			if (userAddresses.Count > 0)
			{
				Address addressToUpdate = userAddresses.FirstOrDefault(i => i.ID == inputModel.ID);

				if (addressToUpdate is not null)
				{
					List<Order> orders = await orderRepository.GetAllByAddressIDAsync(addressToUpdate.ID);
					UserAddressDto result = null;

					if (orders.Count > 0)
					{
						Address newAddress = new()
						{
							FirstName = inputModel.FirstName,
							LastName = inputModel.LastName,
							PhoneNumber = inputModel.PhoneNumber,
							City = inputModel.City,
							Street = inputModel.Street,
							ZipCode = inputModel.ZipCode,
							State = "Česká republika",
							AddressTypeID = addressToUpdate.AddressTypeID,
							LoginAccountID = addressToUpdate.LoginAccountID
						};

						unitOfWork.AddForDelete(addressToUpdate);
						unitOfWork.AddForInsert(newAddress);
						await unitOfWork.CommitAsync();

						result = new()
						{
							ID = newAddress.ID,
							FirstName = newAddress.FirstName,
							LastName = newAddress.LastName,
							PhoneNumber = newAddress.PhoneNumber,
							City = newAddress.City,
							Street = newAddress.Street,
							ZipCode = newAddress.ZipCode
						};
					}
					else
					{
						addressToUpdate.FirstName = inputModel.FirstName;
						addressToUpdate.LastName = inputModel.LastName;
						addressToUpdate.PhoneNumber = inputModel.PhoneNumber;
						addressToUpdate.City = inputModel.City;
						addressToUpdate.Street = inputModel.Street;
						addressToUpdate.ZipCode = inputModel.ZipCode;

						unitOfWork.AddForUpdate(addressToUpdate);
						await unitOfWork.CommitAsync();

						result = new()
						{
							ID = addressToUpdate.ID,
							FirstName = addressToUpdate.FirstName,
							LastName = addressToUpdate.LastName,
							PhoneNumber = addressToUpdate.PhoneNumber,
							City = addressToUpdate.City,
							Street = addressToUpdate.Street,
							ZipCode = addressToUpdate.ZipCode
						};
					}

					return result;
				}
			}

			return null;
		}
	}
}
