using AyCode.Core.Extensions;
using AyCode.Utils.Extensions;
using Mango.Nop.Services;
using Microsoft.AspNetCore.SignalR;
using Nop.Core.Domain.Customers;
using Nop.Data;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Dtos;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Entities;
using Nop.Plugin.Misc.AuctionPlugin.Domains.Enums;
using Nop.Plugin.Misc.AuctionPlugin.Hubs;
using Nop.Plugin.Misc.AuctionPlugin.Hubs.Messages;
using Nop.Services.Logging;

namespace Nop.Plugin.Misc.AuctionPlugin.Services;

public class AuctionBackgroundService : MgBackgroundServiceBase, IBackgroundService
{
    private const int WARNING_STATUS_INTERVAL_SECOND = 15;
    private const string BACKGROUND_TASK_AUCTION_USER_EMAIL = "builtin@background_task_auction.com";

    private readonly IHubContext<AuctionHub> _auctionHubContext;
    private readonly ILockService _lockService;

    private readonly AuctionService _auctionService;
    private readonly Customer _auctionSystemCustomer;

    public AuctionBackgroundService(ILogger logger, IServiceProvider service, IHubContext<AuctionHub> auctionHubContext, AuctionService auctionService, ILockService lockService, IRepository<Customer> customerService) 
        : base(logger, service, WARNING_STATUS_INTERVAL_SECOND * 1000)
    {
        //ExecuteIntervalMs = WARNING_STATUS_INTERVAL_SECOND * 1000;

        _lockService = lockService;
        _auctionService = auctionService;
        _auctionHubContext = auctionHubContext;

        _auctionSystemCustomer = customerService.Table.FirstOrDefault(x => x.Email == BACKGROUND_TASK_AUCTION_USER_EMAIL);
        
        if (_auctionSystemCustomer == null)
            Logger.Error($"AuctionBackgroundService.AuctionBackgroundService(); _auctionSystemCustomer == null; email: {BACKGROUND_TASK_AUCTION_USER_EMAIL}", null, null);
    }

    protected override async Task OnExecuteAsync(CancellationToken stoppingToken)
    {
        if (_auctionSystemCustomer == null) return;

        await Logger.InformationAsync($"AuctionBackgroundService.OnExecuteAsync(); Before lock; ", null, _auctionSystemCustomer);

        using (await _lockService.SemaphoreSlim.UseWaitAsync(stoppingToken))
        {
            if (stoppingToken.IsCancellationRequested) return;

            await Logger.InformationAsync($"AuctionBackgroundService.OnExecuteAsync(); Enter lock;", null, _auctionSystemCustomer);

            var currentAutomaticAuctions = await _auctionService.GetAllCurrentAutoOpenAndClosedAuctionsAsync();
            if (currentAutomaticAuctions.Count > 0)
            {
                await Logger.InformationAsync($"AuctionBackgroundService.OnExecuteAsync(); currentAutomaticAuctions.Count > 0; count: {currentAutomaticAuctions.Count}; names: {string.Join("; ", currentAutomaticAuctions.Select(x => x.AuctionName))}", null, _auctionSystemCustomer);

                var statusChangedMessageWrapper = new MessageWrapper
                {
                    MessageType = nameof(ProductToAuctionStatusNotification),
                    SenderId = _auctionSystemCustomer.Id, //TODO: -1; - J.
                    ResponseType = ResponseType.ToAllClients
                };

                foreach (var automaticAuction in currentAutomaticAuctions)
                {
                    await CheckAndUpdateProductToAuctionsStatusAsync(automaticAuction, statusChangedMessageWrapper);
                    await OpenOrCloseAutomaticAuctionsAsync(automaticAuction, statusChangedMessageWrapper);
                }
            }

            //await _auctionHubContext.Clients.All.SendAsync("OnDateTimeReceive", DateTime.Now.ToString("G")); //TODO: ez csak a teszt időszakig van itt!!! - J.
            await Logger.InformationAsync($"AuctionBackgroundService.OnExecuteAsync(); Exit lock;", null, _auctionSystemCustomer);
        }
    }

    private async Task CheckAndUpdateProductToAuctionsStatusAsync(Auction automaticAuction, MessageWrapper statusChangedMessageWrapper)
    {
        try
        {
            if (automaticAuction.Closed || automaticAuction.EndDateUtc > DateTime.UtcNow.AddSeconds(2 * WARNING_STATUS_INTERVAL_SECOND)) return;

            var activeProductToAuctions = await _auctionService.GetProductToAuctionsByAuctionIdAsync(automaticAuction.Id, true);
            if (activeProductToAuctions.Count == 0) return;

            var auctionDto = new AuctionDto(automaticAuction);
            var changedProductToAuctions = new List<ProductToAuctionMapping>(activeProductToAuctions.Count);
            
            foreach (var activeProductToAuction in activeProductToAuctions)
            {
                var newPtaStatus = activeProductToAuction.AuctionStatus switch
                {
                    AuctionStatus.Active => AuctionStatus.FirstWarning,
                    AuctionStatus.FirstWarning => AuctionStatus.SecondWarning,
                    AuctionStatus.SecondWarning => AuctionStatus.Sold,
                    _ => AuctionStatus.None
                };

                if (newPtaStatus == AuctionStatus.None) continue;

                var responseType = await _auctionService.UpdateProductToAuctionStatusIfValidAsync(newPtaStatus, automaticAuction, activeProductToAuction, _auctionSystemCustomer, false);
                if (responseType == ResponseType.None) continue;

                changedProductToAuctions.Add(activeProductToAuction);
                auctionDto.ProductToAuctionDtos.Add(new ProductToAuctionDto(activeProductToAuction));
            }

            if (changedProductToAuctions.Count == 0) return;

            await _auctionService.UpdateProductToAuctionAsync(changedProductToAuctions);

            statusChangedMessageWrapper.Data = new ProductToAuctionStatusNotification(auctionDto, 0, "EMPTY").ToJson();
            await _auctionHubContext.Clients.All.SendAsync("send", statusChangedMessageWrapper.ToJson());
        }
        catch (Exception ex)
        {
            await Logger.ErrorAsync($"AuctionBackgroundService.CheckAndUpdateProductToAuctionsStatusAsync(); auctionId: {automaticAuction.Id}; auctionName: {automaticAuction.AuctionName}", ex, _auctionSystemCustomer);
        }
    }

    private async Task OpenOrCloseAutomaticAuctionsAsync(Auction automaticAuction, MessageWrapper statusChangedMessageWrapper)
    {
        try
        {
            if (!automaticAuction.Closed)
            {
                var allItemsFinished = (await _auctionService.GetProductToAuctionsByAuctionIdAsync(automaticAuction.Id, false)).All(x => x.AuctionStatus is AuctionStatus.Sold or AuctionStatus.NotSold);

                if (!allItemsFinished) return;

                automaticAuction.Closed = true;
                await _auctionService.UpdateAuctionAsync(automaticAuction);

                //TODO: send message... - J.
                return;
            }

            var productToAuctions = (await _auctionService.GetProductToAuctionsByAuctionIdAsync(automaticAuction.Id, false)).Where(x => x.AuctionStatus == AuctionStatus.None).ToList();
            if (productToAuctions.Count == 0) return;

            automaticAuction.Closed = false;
            var auctionDto = new AuctionDto(automaticAuction);

            foreach (var productToAuction in productToAuctions)
            {
                var responseType = await _auctionService.UpdateProductToAuctionStatusIfValidAsync(AuctionStatus.Active, automaticAuction, productToAuction, _auctionSystemCustomer, false);
                if (responseType == ResponseType.None) continue;

                auctionDto.ProductToAuctionDtos.Add(new ProductToAuctionDto(productToAuction));
            }

            await _auctionService.UpdateAuctionAsync(automaticAuction);
            await _auctionService.UpdateProductToAuctionAsync(productToAuctions);

            statusChangedMessageWrapper.Data = new ProductToAuctionStatusNotification(auctionDto, 0, $"Az aukciót megnyitottuk: {automaticAuction.AuctionName}").ToJson();
            statusChangedMessageWrapper.HideToaster = true;

            await _auctionHubContext.Clients.All.SendAsync("send", statusChangedMessageWrapper.ToJson());
        }
        catch (Exception ex)
        {
            await Logger.ErrorAsync($"AuctionBackgroundService.OpenOrCloseAutomaticAuctionsAsync(); auctionId: {automaticAuction.Id}; name: {automaticAuction.AuctionName}", ex, _auctionSystemCustomer);
        }
    }
}