using Nop.Core;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Plugin.Misc.AuctionPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Dtos;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums;
using Nop.Services.Catalog;
using Nop.Services.Logging;
using Nop.Services.Orders;
using Nop.Services.Payments;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Stores;
using Nop.Core.Domain.Tax;
using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Shipping;
using NUglify.Helpers;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities.Interfaces;

namespace Nop.Plugin.Misc.AuctionPlugin.Services;

/// <summary>
/// Store pickup point service
/// </summary>
public class AuctionService(
    AuctionDbContext ctx,
    IProductService productService,
    IWorkContext workContext,
    //IOrderProcessingService orderProcessingService,
    //IShoppingCartService shoppingCartService,
    //IPaymentService paymentService,
    IStoreContext storeContext,
    IAddressService addressService,
    ICustomerService customerService,
    IOrderService orderService,
    ILogger logger,
    ICategoryService categoryService) : IAuctionService
{
    #region AuctionHub

    public async Task<ResponseType> UpdateProductToAuctionStatusIfValidAsync(AuctionStatus newProductToAuctionStatus, Auction auction, ProductToAuctionMapping productToAuction, Customer customer, bool updateInDatabase = true)
    {
        if (!IsValidRequestAuctionStatus(newProductToAuctionStatus, productToAuction.AuctionStatus))
        {
            logger.Error($"AuctionHub.UpdateProductToAuctionStatusIfValidAsync(); RequestAuctionStatusIsValid() == false; newStatus: {newProductToAuctionStatus}; oldStatus: {productToAuction.AuctionStatus}", null, customer);

            return ResponseType.ToCaller;
        }

        if (newProductToAuctionStatus == AuctionStatus.None) await ResetProductToAuction(productToAuction, auction.CategoryId);
        else
        {
            switch (newProductToAuctionStatus)
            {
                case AuctionStatus.Active:
                    productToAuction.AuctionStatus = AuctionStatus.Active;
                    await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, auction.CategoryId, true);
                    break;

                case AuctionStatus.FirstWarning:
                case AuctionStatus.SecondWarning:
                    productToAuction.AuctionStatus = newProductToAuctionStatus;
                    break;
                
                case AuctionStatus.Sold:
                case AuctionStatus.NotSold:
                    var lastAuctionBid = await GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id);
                    if (lastAuctionBid == null || lastAuctionBid.BidPrice < productToAuction.MinimumPrice) productToAuction.AuctionStatus = AuctionStatus.NotSold;
                    else
                    {
                        productToAuction.AuctionStatus = AuctionStatus.Sold;
                        productToAuction.CurrentPrice = lastAuctionBid.BidPrice;
                        productToAuction.WinnerCustomerId = lastAuctionBid.CustomerId;

                        var placeOrderResult = await CreateOrderForWinnerAsync(productToAuction);
                        if (placeOrderResult == null || placeOrderResult.Id == 0)
                        {
                            logger.Error($"AuctionHub.UpdateProductToAuctionStatusIfValidAsync(); (placeOrderResult == null || placeOrderResult.Id == 0)", null, customer);
                            //return; //TODO: EGYELŐRE HAGYJUK LEZÁRNI AKKOR IS, HA NEM SIKERÜLT AZ ORDERPLACE()! - J.
                        }
                    }

                    await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, auction.CategoryId, false);
                    break;

                case AuctionStatus.Pause:
                    productToAuction.AuctionStatus = AuctionStatus.Pause;
                    break;

                default:
                    logger.Error($"AuctionHub.UpdateProductToAuctionStatusIfValidAsync(); AuctionStatus not found; (newStatus == {newProductToAuctionStatus})", null, customer);
                    return ResponseType.ToCaller;
            }

            if (updateInDatabase)
                await UpdateProductToAuctionAsync(productToAuction);
        }

        return ResponseType.ToAllClients;
    }

    public async Task<(Auction auction, ProductToAuctionMapping productToAuction, ResponseType responseType)> GetAndUpdateProductToAuctionStatusIfValidAsync(int productToAuctionId, AuctionStatus newProductToAuctionStatus, Customer customer)
    {
        var productToAuction = await GetProductToAuctionMappingByIdAsync(productToAuctionId);
        if (productToAuction == null)
        {
            logger.Error($"AuctionHub.GetAndUpdateProductToAuctionStatusIfValidAsync(); (productToAuction == null)", null, customer);
            return (null, null, ResponseType.None);
        }

        var auction = await GetAuctionByIdAsync(productToAuction.AuctionId);
        if (auction == null || auction.Closed)
        {
            logger.Error($"AuctionHub.GetAndUpdateProductToAuctionStatusIfValidAsync(); (auction == null || auction.Closed); Closed: {auction?.Closed}", null, customer);
            return (null, null, ResponseType.None);
        }

        var responseType = await UpdateProductToAuctionStatusIfValidAsync(newProductToAuctionStatus, auction, productToAuction, customer);
        return (auction, productToAuction, responseType);
    }

    public async Task ResetProductToAuction(ProductToAuctionMapping productToAuction, int? categoryId = null)
    {
        productToAuction.AuctionStatus = AuctionStatus.None;
        await ResetProductToAuctionAsync(productToAuction, productToAuction.StartingPrice);

        categoryId ??= (await GetAuctionByIdAsync(productToAuction.AuctionId))?.CategoryId;
        await UpdateProductCategoryIsFeaturedAsync(productToAuction.ProductId, categoryId, false);
    }

    public async Task UpdateProductCategoryIsFeaturedAsync(int productId, int? categoryId, bool isFeatured)
    {
        //Leszarjuk ha elszáll, az aukció menjen tovább... - J.
        try
        {
            if (categoryId.GetValueOrDefault(0) == 0) return;

            var productCategory = (await categoryService.GetProductCategoriesByProductIdAsync(productId)).FirstOrDefault(x => x.CategoryId == categoryId);
            if (productCategory == null) return;

            if (productCategory.IsFeaturedProduct == isFeatured) return;

            productCategory.IsFeaturedProduct = isFeatured;
            await categoryService.UpdateProductCategoryAsync(productCategory);
        }
        catch (Exception ex)
        {
            logger.Error($"AuctionHub.UpdateProductCategoryIsFeaturedAsync(); categoryId: {categoryId}; productId: {productId}; isFeatured: {isFeatured}", ex);
        }
    }

    public bool IsValidRequestAuctionStatus(AuctionStatus newStatus, AuctionStatus oldStatus)
    {
        switch (oldStatus)
        {
            case AuctionStatus.None:
                return newStatus is AuctionStatus.Active or AuctionStatus.Pause;
            case AuctionStatus.Active:
                return newStatus is AuctionStatus.FirstWarning or AuctionStatus.Pause;
            case AuctionStatus.FirstWarning:
                return newStatus is AuctionStatus.SecondWarning or AuctionStatus.Pause;
            case AuctionStatus.SecondWarning:
                return newStatus is AuctionStatus.Sold or AuctionStatus.Pause;
            case AuctionStatus.Pause:
                return newStatus is AuctionStatus.None or AuctionStatus.Active;
            case AuctionStatus.Sold:
            case AuctionStatus.NotSold:
            default:
                return false;
        }
    }
    #endregion

    #region Methods

    public static decimal GetStepAmount(decimal currentPrice)
    {
        return currentPrice switch
        {
            >= 0 and < 100000 => 10000,

            //100 000 - 1 000 000
            >= 100000 and < 200000 => 10000,
            >= 200000 and < 500000 => 20000,
            >= 500000 and < 1000000 => 50000,

            //1 000 000 - 10 000 000
            >= 1000000 and < 2000000 => 100000,
            >= 2000000 and < 5000000 => 200000,
            >= 5000000 and < 10000000 => 500000,

            //10 000 000 - 100 000 000
            >= 10000000 and < 20000000 => 1000000,
            >= 20000000 and < 50000000 => 2000000,
            >= 50000000 and < 100000000 => 5000000,

            //100 000 000 - ~
            >= 100000000 and < 200000000 => 10000000,
            _ => 20000000
        };

        //100 000 Ft – 200 000 Ft között 10 000 Ft-tal
        //200 000 Ft – 500 000 Ft között 20 000 Ft-tal
        //500 000 Ft – 1 000 000 Ft között 50 000 Ft-tal

        //1 000 000 Ft – 2 000 000 Ft között 100 000 Ft-tal
        //2 000 000 Ft – 5 000 000 Ft között 200 000 Ft-tal
        //5 000 000 Ft – 10 000 000 Ft között 500 000 Ft-tal

        //10 000 000 Ft – 20 000 000 Ft között 1 000 000 Ft-tal
        //20 000 000 Ft – 50 000 000 Ft között 2 000 000 Ft-tal
        //50 000 000 Ft – 100 000 000 Ft között 5 000 000 Ft-tal

        //100 000 000 Ft – 200 000 000 Ft között 10 000 000 Ft-tal
        //200 000 000 Ft fölött 20 000 000 Ft-tal
    }

    public async Task<decimal> GetNextBidPrice(decimal currentBidPrice, int productToAuctionId) => GetNextBidPrice(currentBidPrice, GetStepAmount(currentBidPrice), await HasBidByProductToAuctionIdAsync(productToAuctionId));

    public static decimal GetNextBidPrice(decimal currentBidPrice, bool hasAuctionBidInDb) => GetNextBidPrice(currentBidPrice, GetStepAmount(currentBidPrice), hasAuctionBidInDb);

    public static decimal GetNextBidPrice(decimal currentBidPrice, decimal stepAmount, bool hasAuctionBidInDb)
    {
        return hasAuctionBidInDb ? currentBidPrice + stepAmount : currentBidPrice;
    }

    //public async Task ResetProductToAuctionByProductId(int productId)
    //    => await ResetProductToAuctionAsync(await ctx.ProductToAuctions.GetByProductId(productId).FirstOrDefaultAsync());

    public Task ResetProductToAuctionByIdAsync(int productToAuctionId, decimal basePrice = 0)
        => ResetProductToAuctionAsync(ctx.ProductToAuctions.GetById(productToAuctionId), basePrice);

    public Task ResetProductToAuctionAsync(ProductToAuctionMapping productToAuction, decimal basePrice = 0)
        => ctx.ResetProductToAuctionAsync(productToAuction, basePrice);

    public Task<List<AuctionBid>> GetAllLastBidByAuctionIdAsync(int auctionId)
        => ctx.GetAllLastBidByAuctionIdAsync(auctionId);

    public Task<Dictionary<int, AuctionBid>> GetAllLastBidDictionaryByAuctionIdAsync(int auctionId)
        => ctx.GetAllLastBidDictionaryByAuctionIdAsync(auctionId);

    public Task<AuctionBid> GetLastAuctionBidByProductToAuctionIdAsync(int productToAuctionId)
        => ctx.GetLastAuctionBidByProductToAuctionId(productToAuctionId);

    public Task<int> GetBidsCountByProductToAuctionIdAsync(int productToAuctionId)
        => ctx.GetBidsCountByProductToAuctionIdAsync(productToAuctionId);

    public Task<bool> HasBidByProductToAuctionIdAsync(int productToAuctionId)
        => ctx.HasBidByProductToAuctionIdAsync(productToAuctionId);

    public Task<AuctionBid> RevertAuctionBidByProductToAuctionIdAsync(int productToAuctionId)
        => ctx.RevertAuctionBidByProductToAuctionId(productToAuctionId);

    public async Task<Product> GetProductById(int productId)
    {
        var product = await productService.GetProductByIdAsync(productId);
        if (product != null) return product;

        logger.Error($"AuctionService.GetProductById(); (product == null)", null, await workContext.GetCurrentCustomerAsync());
        return null;

    }

    /// <summary>
    /// Gets all bids
    /// </summary>
    /// <param name="customerId">The store identifier; pass 0 to load all records</param>
    /// <param name="pageIndex">Page index</param>
    /// <param name="pageSize">Page size</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the bids
    /// </returns>
    public async Task<IPagedList<AuctionBid>> GetAllBidsAsync(int customerId = 0, int pageIndex = 0, int pageSize = int.MaxValue)
    {
        //TODO: megcsinálni! - J.

        var rez = new List<AuctionBid>();
        //var rez = await _shortTermCacheManager.GetAsync(async () => await _customerBidRepository.GetAllAsync(query =>
        //{
        //    if (customerId > 0)
        //        query = query.Where(bid => bid.CustomerId == customerId || bid.CustomerId == 0);
        //    query = query.OrderBy(bid => bid.CreateDate).ThenBy(bid => bid.ProductId);

        //    return query;
        //}), _pickupPointAllKey, customerId);

        return new PagedList<AuctionBid>(rez, pageIndex, pageSize);
    }

    public async Task<AuctionBid> GetBidByIdAsync(int bidId)
    {
        return await ctx.AuctionBids.GetByIdAsync(bidId);
    }

    public async Task InsertBidAsync(AuctionBid auctionBid)
    {
        //if (await ValidateAuctionBid(auctionBid))
        {
            await ctx.AuctionBids.InsertAsync(auctionBid, false);
        }
    }

    public async Task UpdateBidAsync(AuctionBid auctionBid)
    {
        //if (await ValidateAuctionBid(auctionBid))
        {
            await ctx.AuctionBids.UpdateAsync(auctionBid, false);
        }
    }

    /// <summary>
    /// Deletes a pickup point
    /// </summary>
    /// <param name="pickupPoint">Pickup point</param>
    /// <returns>A task that represents the asynchronous operation</returns>
    public async Task DeleteBidAsync(AuctionBid pickupPoint)
    {
        await ctx.AuctionBids.DeleteAsync(pickupPoint, false);
    }

    private async Task<Address> InsertOrUpdateBillingAddressAsync(Customer customer)
    {
        try
        {
            var billingAddress = await customerService.GetCustomerBillingAddressAsync(customer);
            if (billingAddress == null)
            {
                billingAddress = CreateAddressFromCustomer(customer);
                await addressService.InsertAddressAsync(billingAddress);

                customer.ShippingAddressId = billingAddress.Id;
                customer.BillingAddressId = billingAddress.Id;

                await customerService.UpdateCustomerAsync(customer);
            }
            else
            {
                billingAddress = CopyAddressFromCustomer(billingAddress, customer);
                await addressService.UpdateAddressAsync(billingAddress);
            }

            return billingAddress;
        }
        catch (Exception ex)
        {
            logger.Error($"AuctionService.InsertOrUpdateBillingAddress()", ex, customer);
        }

        return null;
    }

    private static Address CreateAddressFromCustomer(Customer customer)
    {
        var address = new Address();

        return CopyAddressFromCustomer(address, customer);
    }

    private static Address CopyAddressFromCustomer(Address intoAddress, Customer customer)
    {
        intoAddress.FirstName = customer.FirstName;
        intoAddress.LastName = customer.LastName;
        intoAddress.Email = customer.Email;
        intoAddress.FaxNumber = customer.Fax;
        intoAddress.PhoneNumber = customer.Phone;

        intoAddress.CountryId = customer.CountryId;
        intoAddress.City = customer.City;
        intoAddress.County = customer.County;
        intoAddress.Address1 = customer.StreetAddress;
        intoAddress.Address2 = customer.StreetAddress2;
        intoAddress.StateProvinceId = customer.StateProvinceId;
        intoAddress.ZipPostalCode = customer.ZipPostalCode;

        intoAddress.Company = customer.Company;

        return intoAddress;
    }

    public async Task<Order> CreateOrderForWinnerAsync(ProductToAuctionMapping productToAuction)
    {
        if (productToAuction is not { AuctionStatus: AuctionStatus.Sold })
        {
            await logger.InformationAsync($"AuctionService.CreateOrderForWinnerAsync(); (productToAuction is not {{ AuctionStatus: AuctionStatus.Sold }}); productToAuctionId: {productToAuction?.Id}; status: {productToAuction?.AuctionStatus}");
            return null;
        }

        Customer currentCustomer = null;

        try
        {
            currentCustomer = await workContext.GetCurrentCustomerAsync();

            var winnerCustomer = await customerService.GetCustomerByIdAsync(productToAuction.WinnerCustomerId);
            var storeId = winnerCustomer.RegisteredInStoreId;

            var billingAddress = await InsertOrUpdateBillingAddressAsync(winnerCustomer);
            if (billingAddress == null)
            {
                logger.Error($"AuctionService.CreateOrderForWinnerAsync(); (billingAddress == null); productToAuctionId: {productToAuction.Id}", null, currentCustomer);
                return null;
            }

            var customValues = new Dictionary<string, object>
            {
                { "PTAID", $"#{productToAuction.Id}" },
                { "AUKCIÓ", $"#{productToAuction.AuctionId}" },
                { "TÉTELSZÁM", $"#{productToAuction.SortIndex}" }
            };

            if (productToAuction.BiddingNumber != null) customValues.Add("BIDNUM", $"#{productToAuction.BiddingNumber}");

            var orderTotal = productToAuction.CurrentPrice;

            var order = CreateOrder(productToAuction, orderTotal, winnerCustomer, billingAddress, storeId, customValues);
            await orderService.InsertOrderAsync(order);

            if (order.Id > 0) productToAuction.OrderId = order.Id;
            else
            {
                logger.Warning($"AuctionService.CreateOrderForWinnerAsync(); (order.Id <= 0); productToAuctionId: {productToAuction.Id}", null, currentCustomer);
                return null;
            }

            var orderItem = CreateOrderItem(productToAuction, order, orderTotal);
            await orderService.InsertOrderItemAsync(orderItem);

            if (orderItem.Id > 0) productToAuction.OrderItemId = orderItem.Id;
            else
            {
                logger.Warning($"AuctionService.CreateOrderForWinnerAsync(); (orderItem.Id <= 0); productToAuctionId: {productToAuction.Id}", null, currentCustomer);
                return null;
            }

            //var orderNote = CreateOrderNote(order, "...");
            //await orderService.InsertOrderNoteAsync(orderNote);

            //if (orderNote.Id <= 0) logger.Warning($"AuctionService.CreateOrderForWinnerAsync(); (orderNote.Id <= 0); productToAuctionId: {productToAuction.Id}", null, currentCustomer); //TUDATOSAN NINCS RETURN - J.

            return order;
        }
        catch (Exception ex)
        {
            logger.Error($"AuctionService.CreateOrderForWinnerAsync(); Exception; productToAuctionId: {productToAuction.Id}", ex, currentCustomer);
        }

        return null;
    }

    private static OrderItem CreateOrderItem(ProductToAuctionMapping productToAuction, Order order, decimal orderTotal)
    {
        return new OrderItem
        {
            ProductId = productToAuction.ProductId,
            OrderId = order.Id,
            OrderItemGuid = Guid.NewGuid(),
            PriceExclTax = orderTotal,
            PriceInclTax = orderTotal,
            UnitPriceExclTax = orderTotal,
            UnitPriceInclTax = orderTotal,
            Quantity = productToAuction.ProductAmount,
        };
    }

    private static Order CreateOrder(ProductToAuctionMapping productToAuction, decimal orderTotal, Customer customer, Address billingAddress, int storeId, Dictionary<string, object> customValues)
    {
        return new Order
        {
            BillingAddressId = billingAddress.Id,
            CreatedOnUtc = DateTime.UtcNow,
            CurrencyRate = 1,
            CustomOrderNumber = productToAuction.AuctionId + "/" + productToAuction.SortIndex,
            CustomValuesXml = SerializeCustomValuesToXml(customValues),
            CustomerCurrencyCode = "HUF",
            CustomerId = productToAuction.WinnerCustomerId,
            CustomerLanguageId = 2,
            CustomerTaxDisplayType = TaxDisplayType.IncludingTax,
            OrderGuid = Guid.NewGuid(),
            OrderStatus = OrderStatus.Pending,
            OrderTotal = orderTotal,
            PaymentStatus = PaymentStatus.Pending,
            PaymentMethodSystemName = "Payments.CheckMoneyOrder",
            ShippingStatus = ShippingStatus.ShippingNotRequired,
            StoreId = storeId,
            VatNumber = customer.VatNumber,
            CustomerIp = customer.LastIpAddress,
            OrderSubtotalExclTax = orderTotal,
            OrderSubtotalInclTax = orderTotal
        };
    }

    private static OrderNote CreateOrderNote(Order order, string note)
    {
        return new OrderNote
        {
            CreatedOnUtc = order.CreatedOnUtc,
            DisplayToCustomer = true,
            OrderId = order.Id,
            Note = note
        };
    }

    private static string SerializeCustomValuesToXml(Dictionary<string, object> sourceDictionary)
    {
        ArgumentNullException.ThrowIfNull(sourceDictionary);

        if (!sourceDictionary.Any())
            return null;

        var ds = new DictionarySerializer(sourceDictionary);
        var xs = new XmlSerializer(typeof(DictionarySerializer));

        using var textWriter = new StringWriter();
        using (var xmlWriter = XmlWriter.Create(textWriter))
        {
            xs.Serialize(xmlWriter, ds);
        }

        var result = textWriter.ToString();
        return result;
    }

    #region auctions

    public async Task InsertAuctionAsync(Auction auction)
    {
        await ctx.Auctions.InsertAsync(auction, false);
    }

    public async Task UpdateAuctionAsync(Auction auction)
    {
        await ctx.Auctions.UpdateAsync(auction);
    }

    public Task<List<Auction>> GetAllAuctionsAsync() => ctx.Auctions.GetAllAuctions().ToListAsync();
    public Task<List<Auction>> GetAllCurrentAutoOpenAndClosedAuctionsAsync() => ctx.Auctions.GetAllCurrentAutoOpenAndClosedAuctions().ToListAsync();

    public async Task<List<ProductToAuctionMapping>> GetProductToAuctionsByAuctionIdAsync(int auctionId, bool onlyActiveItems)
        => await ctx.ProductToAuctions.GetProductToAuctionsByAuctionId(auctionId, onlyActiveItems).OrderBy(x => x.SortIndex).ToListAsync();

    public Task<ProductToAuctionMapping> GetNextProductToAuctionByAuctionIdAsync(int auctionId)
        => ctx.ProductToAuctions.GetNextItemByAuctionIdAsync(auctionId);

    public Task<List<ProductToAuctionMapping>> GetNotClosedProductToAuctionsByAuctionId(int auctionId)
        => ctx.ProductToAuctions.GetNotClosedItemsByAuctionId(auctionId).OrderBy(x => x.SortIndex).ToListAsync();

    #endregion

    #endregion

    #region Dtos

    public async Task<AuctionDto> GetAuctionDtoWithAuctionBids(int auctionId, bool activeProductOnly, int maxBidsCount = int.MaxValue)
    {
        var auctionDto = await GetAuctionDtoByIdAsync(auctionId, true, activeProductOnly);
        if (auctionDto == null) return null;

        foreach (var auctionDtoProductToAuctionDto in auctionDto.ProductToAuctionDtos)
        {
            auctionDtoProductToAuctionDto.AuctionBidDtos.AddRange(await ctx.AuctionBids.GetAllByProductToAuctionId(auctionDtoProductToAuctionDto.Id).OrderByDescending(x => x.Id).Take(maxBidsCount).Select(x => new AuctionBidDto(x)).ToListAsync());
        }

        return auctionDto;
    }

    public async Task<Auction> GetAuctionByIdAsync(int auctionId) => await ctx.Auctions.GetByIdAsync(auctionId);

    public async Task<AuctionDto> GetAuctionDtoByIdAsync(int auctionId, bool widthProducts, bool activeProductOnly)
    {
        var auction = await GetAuctionByIdAsync(auctionId);
        if (auction == null) return null;

        var auctionDto = new AuctionDto(auction);
        if (widthProducts) auctionDto.ProductToAuctionDtos.AddRange((await GetProductToAuctionDtosByAuctionId(auctionId, activeProductOnly)));

        return auctionDto;
    }

    public async Task<List<ProductToAuctionDto>> GetProductToAuctionDtosByAuctionId(int auctionId, bool activeProductOnly)
    {
        return (await GetProductToAuctionsByAuctionIdAsync(auctionId, activeProductOnly)).Select(x => new ProductToAuctionDto(x)).ToList();
    }

    public async Task<AuctionDto> GetAuctionDtoWithProductByIdAsync(int auctionId, int productId, bool activeProductOnly)
    {
        var auction = await ctx.Auctions.GetByIdAsync(auctionId);
        if (auction == null) return null;

        var auctionDto = new AuctionDto(auction);
        auctionDto.ProductToAuctionDtos.AddRange((await GetProductToAuctionByAuctionIdAndProductIdAsync(auctionId, productId, activeProductOnly)).Select(x => new ProductToAuctionDto(x)));

        return auctionDto;
    }

    public async Task<ProductToAuctionDto> GetProductToAuctionDtoByIdAsync(int productToAuctionId)
    {
        var productToAuction = await ctx.ProductToAuctions.GetByIdAsync(productToAuctionId);
        return productToAuction == null ? null : new ProductToAuctionDto(productToAuction);
    }

    public async Task<AuctionDto> GetAuctionDtoByProductToAuctionIdAsync(int productToAuctionId, bool includeProductToAuctionDto)
    {
        var productTouctionDto = await GetProductToAuctionDtoByIdAsync(productToAuctionId);
        if (productTouctionDto == null) return null;

        var auctionDto = await GetAuctionDtoByIdAsync(productTouctionDto.AuctionId, false, false);

        if (includeProductToAuctionDto) auctionDto.ProductToAuctionDtos.Add(productTouctionDto);
        return auctionDto;
    }

    public async Task<List<ProductToAuctionDto>> GetProductToAuctionDtosByProductIdAsync(int productId)
    {
        return [..await ctx.ProductToAuctions.GetByProductId(productId).Select(x => new ProductToAuctionDto(x)).ToListAsync()];
    }

    public async Task<AuctionBidDto> GetAuctionBidDtoByIdAsync(int auctionBidId)
    {
        var auctionBid = await ctx.AuctionBids.GetByIdAsync(auctionBidId);

        return auctionBid == null ? null : new AuctionBidDto(auctionBid);
    }

    public Task<List<ProductToAuctionMapping>> GetProductToAuctionsByProductIdAsync(int productId)
        => ctx.GetProductToAuctionsByProductIdAsync(productId);

    public Task<List<ProductToAuctionMapping>> GetProductToAuctionByAuctionIdAndProductIdAsync(int auctionId, int productId, bool activeProductOnly)
        => ctx.GetProductToAuctionByAuctionIdAndProductIdAsync(auctionId, productId, activeProductOnly);


    public async Task<ProductToAuctionMapping> AssignProductToAuctionAsync(int productId, decimal startingPrice, decimal bidPrice, int auctionId, int sortIndex)
    {
        var auction = await GetAuctionDtoByIdAsync(auctionId, false, false);
        if (auction == null)
            return null;

        //TODO: itt a ProductToAuctionMapping.Id-t kellene használni! - J.
        var existedProductToAuction = (await GetProductToAuctionByAuctionIdAndProductIdAsync(auctionId, productId, false)).FirstOrDefault();
        if (existedProductToAuction != null) return existedProductToAuction;

        var mapping = new ProductToAuctionMapping
        {
            ProductId = productId,
            StartingPrice = startingPrice,
            CurrentPrice = bidPrice,
            ProductAmount = 1,
            SortIndex = sortIndex,
            AuctionStatus = AuctionStatus.None,
            AuctionId = auctionId
        };

        try
        {
            await ctx.ProductToAuctions.InsertAsync(mapping);
        }
        catch (Exception ex)
        {
            await logger.InformationAsync(ex.ToString());
        }

        return mapping;
    }

    public async Task<ProductToAuctionMapping> GetProductToAuctionMappingByIdAsync(int productToAuctionMappingId)
    {
        return await ctx.ProductToAuctions.GetByIdAsync(productToAuctionMappingId);
    }

    public async Task UpdateProductToAuctionAsync(ProductToAuctionMapping productToAuctionMapping)
    {
        await ctx.ProductToAuctions.UpdateAsync(productToAuctionMapping);
    }

    public async Task UpdateProductToAuctionAsync(IList<ProductToAuctionMapping> productToAuctionMappings)
    {
        await ctx.ProductToAuctions.UpdateAsync(productToAuctionMappings);
    }

    #endregion Dtos
}