using System.Text.Json.Nodes;
using AyCode.Core.Extensions;
using Microsoft.AspNetCore.SignalR;
using Nop.Services.Logging;
using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages;
using Nop.Plugin.Misc.AuctionPlugin.Services;
using AyCode.Utils.Extensions;
using Newtonsoft.Json;
using Nop.Services.Customers;
using Nop.Core;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Services.Catalog;
using Newtonsoft.Json.Linq;
using DocumentFormat.OpenXml.Spreadsheet;
using Microsoft.IdentityModel.Tokens;

namespace Nop.Plugin.Misc.AuctionPlugin.Hubs
{
    public class AuctionHub(SessionService sessionService, ILockService lockService, ILogger logger, IProductService productService, AuctionService auctionService, IWorkContext workContext, ICustomerService customerService)
        : Hub<IAuctionHubClient>
    {
        public override async Task OnConnectedAsync()
        {
            var connectionId = Context.ConnectionId;
            var customer = await workContext.GetCurrentCustomerAsync();
            //if (sessionService.GetOrCreateSessionItem(connectionId) == null) await logger.ErrorAsync($"AuctionHub.OnConnectedAsync(); (sessionItem == null); connectionId: {connectionId}");

            //await _logger.InformationAsync($"AuctionHub.OnConnectedAsync(); Caller connected  with id: {connectionId}");

            var httpContext = Context.GetHttpContext();
            if (httpContext == null) await logger.ErrorAsync($"AuctionHub.OnConnectedAsync(); (httpContext == null); connectionId: {connectionId}");
            else
            {
                var sessionItem = sessionService.GetOrCreateSessionItem(httpContext.Session.Id);

                if (sessionItem == null) await logger.ErrorAsync($"AuctionHub.OnConnectedAsync(); (sessionItem == null); connectionId: {connectionId}; sessionId: {httpContext.Session.Id}");
                else
                {
                    sessionItem.CustomerId = customer.Id;
                    sessionItem.SignaRConnectionId = connectionId;
                }

                var userName = httpContext.Request.Query["ConnectionId"];
                if (!userName.IsNullOrEmpty())
                {
                    await logger.InformationAsync($"AuctionHub.OnConnectedAsync(); Caller connected with name: {userName}; connectionId: {connectionId}");
                }
            }

            await base.OnConnectedAsync();
            await Clients.Caller.OnConnected(connectionId);
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var httpContext = Context.GetHttpContext();

            if (httpContext == null) await logger.ErrorAsync($"AuctionHub.OnDisconnectedAsync(); (httpContext == null); connectionId: {Context.ConnectionId}");
            else sessionService.TryRemoveSessionItem(httpContext.Session.Id, out _);

            await base.OnDisconnectedAsync(exception);
        }

        public async Task ReceiveMessageFromClient(MessageWrapper messageWrapper)
        {
            if (!TryGetCurrentSessionItem(out var sessionItem) || sessionItem == null)
            {
                await logger.ErrorAsync($"AuctionHub.ReceiveMessageFromClient(); (TryGetCurrentSessionItem(out var sessionItem) == false || sessionItem == null); connectionId: {Context.ConnectionId}");
                return;
            }

            sessionItem.RequestCount++;
            await HandleMessageAsync(messageWrapper, sessionItem, Context.ConnectionId);
        }

        public async Task<ResponseType> HandleMessageAsync(MessageWrapper messageWrapper, SessionItem sessionItem, string connectionId)
        {
            var customer = await workContext.GetCurrentCustomerAsync();
            //var connectionId = sessionItem?.SessionId;

            //ArgumentNullException.ThrowIfNull(messageWrapper?.Data);

            if (messageWrapper?.Data == null)
            {
                logger.Error($"AuctionHub.HandleMessageAsync(); message?.Data == null; connectionId: {connectionId}", null, customer);
                return ResponseType.Error;
            }

            if (sessionItem == null || sessionItem.RequestCount != messageWrapper.RequestCount)
                await logger.WarningAsync($"AuctionHub.HandleMessageAsync(); sessionItem == null || sessionItem.RequestCount != messageWrapper.RequestCount; connectionId: {connectionId}; sessionId: {sessionItem?.SessionId}; sessionRequests: {sessionItem?.RequestCount}; wrapperRequests: {messageWrapper.RequestCount}", null, customer);

            if (customer == null || messageWrapper.SenderId <= 0 || messageWrapper.SenderId != customer.Id || await customerService.IsGuestAsync(customer))
            {
                logger.Error($"AuctionHub.HandleMessageAsync(); (customer == null || message.SenderId <= 0 || message.SenderId != customer.Id || IsGuestAsync() == true); connectionId: {connectionId}", null, customer);
                return ResponseType.Error;
            }

            await logger.InformationAsync($"AuctionHub.HandleMessageAsync(); Before lock; MessageType: {messageWrapper.MessageType}; connectionId: {connectionId}; jsonData: {messageWrapper.Data}", null, customer);

            //TODO: az összes request-et egy base-ből származtatni és beletenni az AuctionRequestMode-ot! - J.
            using (await lockService.SemaphoreSlim.UseWaitAsync())
            {
                try
                {
                    await logger.InformationAsync($"AuctionHub.HandleMessageAsync(); Enter lock; MessageType: {messageWrapper.MessageType}; connectionId: {connectionId}", null, customer);

                    //await Task.Delay(3000); //TODO: ez csak a teszt időszakig van itt!!! - J.

                    if (messageWrapper.MessageType == "BidRequestMessage") return await HandleBidRequestAsync(customer, messageWrapper);
                    else
                    {
                        if (!customer.IsSystemAccount && !await customerService.IsAdminAsync(customer))
                        {
                            logger.Error($"AuctionHub.HandleProductToAuctionStatusChangedRequest(); (!customer.IsSystemAccount && !await customerService.IsAdminAsync(customer)); connectionId: {connectionId}", null, customer);
                            return ResponseType.Error;
                        }

                        switch (messageWrapper.MessageType)
                        {
                            case nameof(AuctionProductStatusRequest):
                                return await HandleProductToAuctionStatusChangedRequest(customer, messageWrapper);

                            case nameof(RevertAuctionBidRequest):
                                return await HandleRevertAuctionBidRequest(customer, messageWrapper);

                            default:
                                await logger.ErrorAsync($"AuctionHub.HandleMessageAsync(); Unknown message type; MessageType: {messageWrapper.MessageType}; connectionId: {connectionId}", null, customer);
                                return ResponseType.Error;
                        }
                    }
                }
                catch (Exception ex)
                {
                    logger.Error($"AuctionHub.HandleMessageAsync(); switch (message.MessageType); MessageType: {messageWrapper.MessageType}; connectionId: {connectionId}; Exception: {ex.Message}", ex, customer);
                }
                finally
                {
                    await logger.InformationAsync($"AuctionHub.HandleMessageAsync(); Exit lock; MessageType: {messageWrapper.MessageType}; connectionId: {connectionId}", null, customer);
                }
            }

            return ResponseType.Error;
        }

        public async Task<ResponseType> HandleRevertAuctionBidRequest(Customer customer, MessageWrapper messageWrapper)
        {
            var revertAuctionBidRequest = messageWrapper.Data.JsonTo<RevertAuctionBidRequest>();
            if (revertAuctionBidRequest == null)
            {
                logger.Error($"AuctionHub.HandleRevertAuctionBidRequest(); auctionProductStatusRequest == null", null, customer);
                return ResponseType.Error;
            }

            try
            {
                var productToAuction = (await auctionService.GetProductToAuctionMappingByIdAsync(revertAuctionBidRequest.ProductToAuctionId));
                if (productToAuction == null)
                {
                    logger.Error($"AuctionHub.HandleRevertAuctionBidRequest(); (productToAuction == null); ptaId: {revertAuctionBidRequest.ProductToAuctionId}", null, customer);
                    return ResponseType.Error;
                }

                await logger.InformationAsync($"AuctionHub.HandleRevertAuctionBidRequest(); (productToAuction is not {{ AuctionStatus: AuctionStatus.Pause }}); AuctionStatus: {productToAuction.AuctionStatus}; ptaId: {revertAuctionBidRequest.ProductToAuctionId}", null, customer);

                var product = await auctionService.GetProductById(productToAuction.ProductId);
                if (product == null)
                {
                    logger.Error($"AuctionHub.HandleBidRequestAsync(); (product == null);", null, customer);
                    return ResponseType.Error;
                }

                if (productToAuction.AuctionStatus != AuctionStatus.Pause)
                {
                    logger.Warning($"AuctionHub.HandleRevertAuctionBidRequest(); (productToAuction.AuctionStatus != AuctionStatus.Pause); AuctionStatus: {productToAuction.AuctionStatus}; ptaId: {revertAuctionBidRequest.ProductToAuctionId}", null, customer);

                    await SendAuctionBidMessageAsync(messageWrapper, productToAuction, product, customer.Id, ResponseType.ToCaller);
                    return ResponseType.Error;
                }

                var revertLastBid = await auctionService.RevertAuctionBidByProductToAuctionIdAsync(productToAuction.Id);

                if (revertLastBid == null) await auctionService.ResetProductToAuction(productToAuction);
                else
                {
                    productToAuction.BidsCount = await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id);
                    await SetAuctionBidPriceAsync(revertLastBid.BidPrice, productToAuction, product, revertLastBid.CustomerId);
                }

                await SendAuctionBidMessageAsync(messageWrapper, productToAuction, product, customer.Id, ResponseType.ToAllClients);
                return ResponseType.ToAllClients;
            }
            catch (Exception ex)
            {
                logger.Error($"AuctionHub.HandleRevertAuctionBidRequest(); ptaId: {revertAuctionBidRequest.ProductToAuctionId}; Exception: {ex.Message}", ex, customer);
            }

            return ResponseType.Error;
        }

        public async Task<ResponseType> HandleBidRequestAsync(Customer customer, MessageWrapper messageWrapper)
        {
            var bidRequestMessage = messageWrapper.Data.JsonTo<AuctionBidRequest>();
            if (bidRequestMessage == null)
            {
                logger.Error($"AuctionHub.HandleBidRequestAsync(); (bidRequestMessage == null)", null, customer);
                return ResponseType.Error;
            }

            try
            {
                await logger.InformationAsync($"AuctionHub.HandleBidRequestAsync(); Bid received; Auction: {bidRequestMessage.AuctionId}; ptaId: {bidRequestMessage.ProductAuctionMappingId}; Product: {bidRequestMessage.ProductId}; Bid: {bidRequestMessage.BidPrice}; Customer: {bidRequestMessage.CustomerId}", null, customer);

                var auction = await auctionService.GetAuctionDtoByIdAsync(bidRequestMessage.AuctionId, false, false);
                if (auction == null || auction.Closed)
                {
                    logger.Error($"AuctionHub.HandleBidRequestAsync(); (auction == null || auction.Closed); Closed: {auction?.Closed}", null, customer);
                    return ResponseType.Error;
                }

                var product = await auctionService.GetProductById(bidRequestMessage.ProductId);
                if (product == null)
                {
                    logger.Error($"AuctionHub.HandleBidRequestAsync(); (product == null);", null, customer);
                    return ResponseType.Error;
                }

                //var activeProductAuction = (await auctionService.GetProductToAuctionByAuctionIdAndProductIdAsync(bidRequestMessage.AuctionId, bidRequestMessage.ProductId, true)).FirstOrDefault();
                var activeProductAuction = (await auctionService.GetProductToAuctionMappingByIdAsync(bidRequestMessage.ProductAuctionMappingId));
                if (activeProductAuction is not { IsActiveItem: true } || (activeProductAuction.WinnerCustomerId == customer.Id && !await customerService.IsAdminAsync(customer)))
                {
                    logger.Warning($"AuctionHub.HandleBidRequestAsync(); (activeProductAuction is not {{ IsActiveItem: true }} || activeProductAuction.WinnerCustomerId == customer.Id && !await customerService.IsAdminAsync(customer)); ptaId: {bidRequestMessage.ProductAuctionMappingId}; AuctionStatus: {activeProductAuction?.AuctionStatus}; WinnerCustomerId: {activeProductAuction?.WinnerCustomerId}", null, customer);

                    await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToCaller);
                    return ResponseType.Error;
                }

                if (!await IsValidProductPricesAndSyncIfNeedAsync(activeProductAuction, product, customer))
                {
                    await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToAllClients);
                    return ResponseType.Error;
                }

                if (!IsValidBidPrice(activeProductAuction, bidRequestMessage.BidPrice, customer))
                {
                    await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToCaller);
                    return ResponseType.Error;
                }

                var auctionBid = bidRequestMessage.CreateMainEntity();
                auctionBid.AuctionId = auction.Id;
                auctionBid.ProductAuctionMappingId = activeProductAuction.Id;

                await auctionService.InsertBidAsync(auctionBid);
                activeProductAuction.BidsCount++;

                activeProductAuction.AuctionStatus = AuctionStatus.Active;
                if (!await SetAuctionBidPriceAsync(auctionBid.BidPrice, activeProductAuction, product, customer.Id))
                {
                    await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToCaller);
                    return ResponseType.Error;
                }

                await SendAuctionBidMessageAsync(messageWrapper, activeProductAuction, product, customer.Id, ResponseType.ToAllClients);

                return ResponseType.ToAllClients;
            }
            catch (Exception ex)
            {
                logger.Error($"AuctionHub.HandleBidRequestAsync(); ptaId: {bidRequestMessage.ProductAuctionMappingId}; Exception: {ex.Message}", ex, customer);
            }

            return ResponseType.Error;
        }

        public async Task<ResponseType> HandleProductToAuctionStatusChangedRequest(Customer customer, MessageWrapper statusChangedMessageWrapper)
        {
            var auctionProductStatusRequest = statusChangedMessageWrapper.Data.JsonTo<AuctionProductStatusRequest>();
            if (auctionProductStatusRequest == null)
            {
                logger.Error($"AuctionHub.HandleProductToAuctionStatusChangedRequest(); auctionProductStatusRequest == null", null, customer);
                return ResponseType.None;
            }

            try
            {
                //var responseType = ResponseType.ToAllClients;
                await logger.InformationAsync($"AuctionHub.HandleProductToAuctionStatusChangedRequest(); ptaId: {auctionProductStatusRequest.ProductToAuctionId}; Status: {auctionProductStatusRequest.AuctionStatus}({(int)auctionProductStatusRequest.AuctionStatus})", null, customer);

                var (auction, productToAuction, responseType) = await auctionService.GetAndUpdateProductToAuctionStatusIfValidAsync(auctionProductStatusRequest.ProductToAuctionId, auctionProductStatusRequest.AuctionStatus, customer);
                if (auction == null || productToAuction == null || responseType == ResponseType.None) return ResponseType.None;

                await SendStatusChangedNotificationAsync(customer, statusChangedMessageWrapper, productToAuction, responseType);

                //TODO: gond van ha az Admin valamit módosít és újrazár egy régi ProductToAuction-t!!!! AuctionRequestMode.Normal...- J.
                if ( /*AuctionRequestMode.Normal &&*/ auction.AuctionType == AuctionType.AutomaticNext && productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold)
                    await StepNextProductToAuctionAsync(statusChangedMessageWrapper, customer, productToAuction, auctionProductStatusRequest);

                return ResponseType.ToAllClients;
            }
            catch (Exception ex)
            {
                logger.Error($"AuctionHub.HandleProductToAuctionStatusChangedRequest(); ptaId: {auctionProductStatusRequest.ProductToAuctionId}; Exception: {ex.Message}", ex, customer);
            }

            return ResponseType.Error;
        }

        //private async Task<ResponseType> UpdateProductToAuctionStatusIfValidAsync(AuctionStatus newProductToAuctionStatus, Auction auction, ProductToAuctionMapping productToAuction, Customer customer)
        //{
        //    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:
        //                var lastAuctionBid = await auctionService.GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id);
        //                if (lastAuctionBid == null) productToAuction.AuctionStatus = AuctionStatus.NotSold;
        //                else
        //                {
        //                    productToAuction.AuctionStatus = AuctionStatus.Sold;
        //                    productToAuction.CurrentPrice = lastAuctionBid.BidPrice;
        //                    productToAuction.WinnerCustomerId = lastAuctionBid.CustomerId;

        //                    var placeOrderResult = await auctionService.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;
        //        }

        //        await auctionService.UpdateProductToAuctionMappingAsync(productToAuction);
        //    }

        //    return ResponseType.ToAllClients;
        //}

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

        //    var auction = await auctionService.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);
        //}

        private async Task SendStatusChangedNotificationAsync(Customer customer, MessageWrapper statusChangedMessageWrapper, ProductToAuctionMapping productToAuction, ResponseType responseType)
        {
            await UpdateStatusChangedNotificationMessageWrapperAsync(statusChangedMessageWrapper, customer, productToAuction, responseType);
            await SendMessageWrapperAsync(statusChangedMessageWrapper);
        }

        private async Task StepNextProductToAuctionAsync(MessageWrapper statusChangedMessageWrapper, Customer customer, ProductToAuctionMapping productToAuction, AuctionProductStatusRequest auctionProductStatusRequest)
        {
            var nextProductToAuction = await auctionService.GetNextProductToAuctionByAuctionIdAsync(productToAuction.AuctionId);
            if (nextProductToAuction == null) return;

            await logger.InformationAsync($"AuctionHub.StepNextProductToAuctionAsync(); NextProductToAuctionId: {nextProductToAuction.Id};", null, customer);

            if (nextProductToAuction.AuctionStatus != AuctionStatus.None)
            {
                await logger.WarningAsync($"AuctionHub.StepNextProductToAuctionAsync(); (nextProductToAuction.AuctionStatus != AuctionStatus.None); NextProductToAuctionId: {nextProductToAuction.Id}; Status: {nextProductToAuction.AuctionStatus}({(int)nextProductToAuction.AuctionStatus})", null, customer);
                return; //TODO: Ilyenkor mi legyen?! - J.
            }

            auctionProductStatusRequest.AuctionStatus = AuctionStatus.Active;
            auctionProductStatusRequest.ProductToAuctionId = nextProductToAuction.Id;
            statusChangedMessageWrapper.Data = auctionProductStatusRequest.ToJson();

            await HandleProductToAuctionStatusChangedRequest(customer, statusChangedMessageWrapper);
        }

        private async Task<bool> SetAuctionBidPriceAsync(decimal bidPrice, ProductToAuctionMapping productToAuction, Product product, int lastBidCustomerId)
        {
            try
            {
                product.Price = bidPrice;
                await productService.UpdateProductAsync(product);

                //activeProductAuction.StartingPrice = product.OldPrice; //TODO: ez biztosan kezelve van mikor összerendeljük? - J.
                productToAuction.CurrentPrice = bidPrice;
                productToAuction.WinnerCustomerId = lastBidCustomerId;
                await auctionService.UpdateProductToAuctionAsync(productToAuction);

                return true;
            }
            catch (Exception ex)
            {
                logger.Error($"AuctionHub.SetAuctionBidPriceAsync();", ex);
            }

            return false;
        }

        private async Task<bool> IsValidProductPricesAndSyncIfNeedAsync(ProductToAuctionMapping productToAuction, Product product, Customer customer = null)
        {
            if (product.Price == productToAuction.CurrentPrice) return true;

            if (productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold)
            {
                await logger.ErrorAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); (product.Price != productToAuction.CurrentPrice && (productToAuction.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold)); productPrice: {product.Price}; productToAuctionCurrentPrice: {productToAuction.CurrentPrice}; auctionStatus: {productToAuction.AuctionStatus}", null, customer);
                return false;
            }

            await logger.ErrorAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); (product.Price != productToAuction.CurrentPrice); productPrice: {product.Price}; productToAuctionCurrentPrice: {productToAuction.CurrentPrice}", null, customer);

            var lastBidPrice = product.OldPrice;
            var lastBid = await auctionService.GetLastAuctionBidByProductToAuctionIdAsync(productToAuction.Id);

            if (lastBid == null)
            {
                productToAuction.StartingPrice = lastBidPrice;
                productToAuction.BidsCount = 0;
            }
            else lastBidPrice = lastBid.BidPrice;

            await SetAuctionBidPriceAsync(lastBidPrice, productToAuction, product, lastBid?.CustomerId ?? 0);

            await logger.InformationAsync($"AuctionHub.SyncProductPricesIfNeedAsync(); Synchronize the price was successful! lastBidPrice: {lastBidPrice};", null, customer);
            return false;
        }

        private bool IsValidBidPrice(ProductToAuctionMapping productToAuction, decimal bidRequestPrice, Customer customer = null)
        {
            var hasAuctionBidInDb = productToAuction.BidsCount > 0;
            var nextBidPrice = AuctionService.GetNextBidPrice(productToAuction.CurrentPrice, hasAuctionBidInDb);

            if (bidRequestPrice != nextBidPrice)
            {
                logger.Warning($"AuctionHub.IsValidBidPrice(); (bidRequestPrice != nextBidPrice); productToAuctionCurrentPrice: {productToAuction.CurrentPrice}; bidRequestPrice: {bidRequestPrice}; hasAuctionBidInDb: {hasAuctionBidInDb}", null, customer);
                return false;
            }

            return true;
        }

        private async Task UpdateStatusChangedNotificationMessageWrapperAsync(MessageWrapper statusChangedMessageWrapper, Customer customer, ProductToAuctionMapping productToAuction, ResponseType responseType)
        {
            var bidsCount = productToAuction.BidsCount;

            statusChangedMessageWrapper.MessageType = nameof(ProductToAuctionStatusNotification);
            statusChangedMessageWrapper.SenderId = customer.Id;
            statusChangedMessageWrapper.ResponseType = responseType;
            statusChangedMessageWrapper.Data = new ProductToAuctionStatusNotification(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true), bidsCount, "EMPTY").ToJson();
        }

        public async Task UpdateBidNotificationMessageWrapperAsync(MessageWrapper messageWrapper, ProductToAuctionMapping productToAuction, Product product, int customerId, ResponseType responseType)
        {
            var bidsCount = productToAuction.BidsCount; //await auctionService.GetBidsCountByProductToAuctionIdAsync(productToAuction.Id);

            var stepAmount = AuctionService.GetStepAmount(productToAuction.CurrentPrice);
            var nextBidPrice = AuctionService.GetNextBidPrice(productToAuction.CurrentPrice, stepAmount, bidsCount > 0);

            messageWrapper.SenderId = customerId;
            messageWrapper.ResponseType = responseType;
            messageWrapper.MessageType = "bidNotification";
            messageWrapper.Data = new BidNotificationMessage(await auctionService.GetAuctionDtoByProductToAuctionIdAsync(productToAuction.Id, true), bidsCount, "EMPTY") //TODO: NE KÉRJÜK LE DB-BŐL!!! - J.
            {
                ProductName = product.Name,
                CurrentPrice = productToAuction.CurrentPrice,
                NextStepAmount = stepAmount,
                NextBidPrice = nextBidPrice,
            }.ToJson();
        }

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

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

        //private 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 async Task SendAuctionBidMessageAsync(MessageWrapper messageWrapper, ProductToAuctionMapping productToAuction, Product product, int customerId, ResponseType responseType)
        {
            await UpdateBidNotificationMessageWrapperAsync(messageWrapper, productToAuction, product, customerId, responseType);
            await SendMessageWrapperAsync(messageWrapper);
        }

        public async Task SendMessageWrapperAsync(MessageWrapper messageWrapper)
        {
            await logger.InformationAsync($"AuctionHub.SendMessageWrapperAsync(); MessageType: {messageWrapper.MessageType}; ResponseType: {messageWrapper.ResponseType}; jsonData: {messageWrapper.Data}");

            switch (messageWrapper.ResponseType)
            {
                case ResponseType.ToCaller:
                    messageWrapper.HideToaster = true;
                    await Clients.Caller.send(messageWrapper.ToJson());

                    break;
                case ResponseType.ToAllClients:
                    var messageWrapperJson = messageWrapper.ToJson();

                    if (messageWrapper.HideToaster) await Clients.All.send(messageWrapperJson);
                    else
                    {
                        //messageWrapperJObject["senderId" /*nameof(MessageWrapper.SenderId)*/] = "-1";
                        await Clients.Others.send(messageWrapperJson);
                        
                        var messageWrapperJObject = JObject.Parse(messageWrapperJson);
                        messageWrapperJObject["hideToaster" /*nameof(MessageWrapper.HideToaster)*/] = "true";
                        
                        await Clients.Caller.send(messageWrapperJObject.ToString(Formatting.None));
                    }

                    break;
                case ResponseType.Error:
                    messageWrapper.HideToaster = false;
                    await Clients.Caller.send(messageWrapper.ToJson());

                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        //private async Task SendMessageToClientsAsync(MessageWrapper messageWrapper, bool sendToCaller = false)
        //{
        //    if (sendToCaller) await Clients.Caller.send(messageWrapper.ToJson());
        //    else await Clients.All.send(messageWrapper.ToJson());
        //}

        //private static 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;
        //    }
        //}

        private bool TryGetCurrentSessionItem(out SessionItem sessionItem)
        {
            sessionItem = null;

            var httpContext = Context.GetHttpContext();
            return httpContext != null && sessionService.TryGetSessionItem(httpContext.Session.Id, out sessionItem);
        }
    }
}
