using AyCode.Core.Loggers;
using AyCode.Services.SignalRs;
using DocumentFormat.OpenXml.Drawing;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.Interfaces;
using FruitBank.Common.Server.Interfaces;
using FruitBank.Common.Server.Services.SignalRs;
using FruitBank.Common.SignalRs;
using Mango.Nop.Core.Loggers;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Events;
using Nop.Plugin.Misc.FruitBankPlugin.Controllers;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Catalog;

namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;

public class CustomOrderSignalREndpoint(FruitBankDbContext ctx, SignalRSendToClientService sendToClient, IPriceCalculationService customPriceCalculationService,IEventPublisher eventPublisher, IWorkContext workContext, IEnumerable<IAcLogWriterBase> logWriters) 
    : ICustomOrderSignalREndpointServer
{
    private const int LastOrderDays = 15;
    private readonly ILogger _logger = new Logger<CustomOrderSignalREndpoint>(logWriters.ToArray());

    [SignalR(SignalRTags.GetAllOrderDtos)]
    public async Task<List<OrderDto>> GetAllOrderDtos()
    {
        _logger.Detail($"GetAllOrderDtos invoked; lastDaysCount: {LastOrderDays}");

        var fromDateUtc = DateTime.UtcNow.Date.AddDays(-LastOrderDays);
        return await ctx.OrderDtos.GetAll(true).Where(o => o.CreatedOnUtc >= fromDateUtc).ToListAsync();
    }

    [SignalR(SignalRTags.GetOrderDtoById)]
    public async Task<OrderDto> GetOrderDtoById(int orderId)
    {
        return await ctx.OrderDtos.GetByIdAsync(orderId, true);
    }

    [SignalR(SignalRTags.GetPendingOrderDtos)]
    public async Task<List<OrderDto>> GetPendingOrderDtos()
    {
        return await ctx.OrderDtos.GetAllByOrderStatus(OrderStatus.Pending).ToListAsync();
    }

    [SignalR(SignalRTags.GetPendingOrderDtosForMeasuring)]
    public async Task<List<OrderDto>> GetPendingOrderDtosForMeasuring(int lastDaysCount)
    {
        _logger.Detail($"GetPendingOrderDtosForMeasuring invoked; lastDaysCount: {lastDaysCount}");

        var startTime = DateTime.Now;
        var fromDate = DateTime.Now.AddDays(-lastDaysCount);

        var orders = await ctx.OrderDtos.GetAllForMeasuring(fromDate).ToListAsync();
        _logger.Detail($"GetPendingOrderDtosForMeasuring; ordersCount: {orders.Count}; dbResponse: {(DateTime.Now - startTime).TotalSeconds} sec.");

        return orders;
    }

    [SignalR(SignalRTags.GetOrderDatesForMeasuring)]
    public async Task<List<OrderDto>> GetOrderDatesForMeasuring(int lastDaysCount)
    {
        var fromDate = DateTime.Now.AddDays(-lastDaysCount);
        //return await ctx.OrderDtos.GetAllForMeasuring(fromDate).Select(x => new MeasuringOrderDate(...)).ToListAsync();
        return null;
    }

    [SignalR(SignalRTags.GetAllOrderDtoByIds)]
    public async Task<List<OrderDto>> GetAllOrderDtoByIds(int[] orderIds)
    {
        return await ctx.OrderDtos.GetAllByIds(orderIds).ToListAsync();
    }

    [SignalR(SignalRTags.GetAllOrderDtoByProductId)]
    public async Task<List<OrderDto>> GetAllOrderDtoByProductId(int productId)
    {
        return await ctx.OrderDtos.GetAllByProductId(productId, true).ToListAsync();
    }

    [SignalR(SignalRTags.GetOrderItemDtoById)]
    public async Task<OrderItemDto> GetOrderItemDtoById(int orderItemId)
    {
        return await ctx.OrderItemDtos.GetByIdAsync(orderItemId, true);
    }

    [SignalR(SignalRTags.GetAllOrderItemDtos)]
    public async Task<List<OrderItemDto>> GetAllOrderItemDtos()
    {
        _logger.Detail($"GetAllOrderItemDtos invoked; lastDaysCount: {LastOrderDays}");

        var fromDateUtc = DateTime.UtcNow.Date.AddDays(-LastOrderDays);
        return await ctx.OrderItemDtos.GetAll(true).Where(oi => oi.OrderDto == null || oi.OrderDto.CreatedOnUtc >= fromDateUtc).ToListAsync();
    }

    [SignalR(SignalRTags.GetAllOrderItemDtoByOrderId)]
    public async Task<List<OrderItemDto>> GetAllOrderItemDtoByOrderId(int orderId)
    {
        return await ctx.OrderItemDtos.GetAllByOrderId(orderId, true).ToListAsync();
    }

    [SignalR(SignalRTags.GetAllOrderItemDtoByProductId)]
    public async Task<List<OrderItemDto>> GetAllOrderItemDtoByProductId(int productId)
    {
        return await ctx.OrderItemDtos.GetAllByProductId(productId, true).ToListAsync();
    }

    [SignalR(SignalRTags.GetOrderItemPalletById)]
    public async Task<OrderItemPallet> GetOrderItemPalletById(int orderItemPalletId)
    {
        return await ctx.OrderItemPallets.GetByIdAsync(orderItemPalletId, true);
    }

    [SignalR(SignalRTags.GetAllOrderItemPallets)]
    public async Task<List<OrderItemPallet>> GetAllOrderItemPallets()
    {
        _logger.Detail($"GetAllOrderItemPallets invoked; lastDaysCount: {LastOrderDays}");

        var fromDateUtc = DateTime.UtcNow.Date.AddDays(-LastOrderDays);
        return await ctx.OrderItemPallets.GetAll(true).Where(oip => oip.OrderItemDto.OrderDto == null || oip.OrderItemDto.OrderDto.CreatedOnUtc >= fromDateUtc).ToListAsync();
    }
    [SignalR(SignalRTags.GetAllOrderItemPalletByOrderItemId)]
    public async Task<List<OrderItemPallet>> GetAllOrderItemPalletByOrderItemId(int orderItemId)
    {
        return await ctx.OrderItemPallets.GetAllByOrderItemId(orderItemId, true).ToListAsync();
    }

    [SignalR(SignalRTags.GetAllOrderItemPalletByOrderId)]
    public async Task<List<OrderItemPallet>> GetAllOrderItemPalletByOrderId(int orderId)
    {
        return await ctx.OrderItemPallets.GetAllByOrderId(orderId).ToListAsync();
    }

    [SignalR(SignalRTags.GetAllOrderItemPalletByProductId)]
    public async Task<List<OrderItemPallet>> GetAllOrderItemPalletByProductId(int productId)
    {
        return await ctx.OrderItemPallets.GetAllByProductId(productId).ToListAsync();
    }
    
    [SignalR(SignalRTags.StartMeasuring, SignalRTags.SendOrderChanged, SendToClientType.Others)]
    public async Task<OrderDto> StartMeasuring(int orderId, int userId)
    {
        _logger.Detail($"StartMeasuring invoked; orderId: {orderId}; userId: {userId}");

        if (!await ctx.StartMeasuringSafeAsync(orderId, userId)) return null;
        return await ctx.OrderDtos.GetByIdAsync(orderId, true);
    }

    [SignalR(SignalRTags.SetOrderStatusToComplete, SignalRTags.SendOrderChanged, SendToClientType.Others)]
    public async Task<OrderDto> SetOrderStatusToComplete(int orderId, int revisorId)
    {
        _logger.Detail($"SetOrderStatusToComplete invoked; orderId: {orderId}; revisorId: {revisorId}");

        //TODO: Ez egész nagyon kusza, átgondolni és refaktorálni! - J.
        var order = ctx.Orders.GetById(orderId);
        var prevOrderStatus = order.OrderStatus;

        if (order.OrderStatus == OrderStatus.Complete || !await ctx.SetOrderStatusToCompleteSafeAsync(orderId, revisorId)) return null;

        order.OrderStatus = OrderStatus.Complete;
        await ((CustomPriceCalculationService)customPriceCalculationService).CheckAndUpdateOrderTotalPrice(order);
        
        if (prevOrderStatus != order.OrderStatus) await eventPublisher.PublishAsync(new OrderStatusChangedEvent(order, prevOrderStatus));

        return await ctx.OrderDtos.GetByIdAsync(orderId, true);
    }

    [SignalR(SignalRTags.AddOrUpdateMeasuredOrderItemPallet, SignalRTags.SendOrderItemPalletChanged, SendToClientType.Others)]
    public async Task<OrderItemPallet> AddOrUpdateMeasuredOrderItemPallet(OrderItemPallet orderItemPallet)
    {
        ArgumentNullException.ThrowIfNull(orderItemPallet);

        _logger.Detail($"AddOrUpdateMeasuredOrderItemPallet invoked; {orderItemPallet}");

        if (!await ctx.AddOrUpdateOrderItemPalletSafeAsync(orderItemPallet)) return null;

        var isMeasurable = orderItemPallet.GrossWeight > 0;
        if (isMeasurable)
        {
            var orderItemDto = await ctx.OrderItemDtos.GetByIdAsync(orderItemPallet.OrderItemId, true);

            if (orderItemDto.IsAudited && orderItemDto.IsMeasurable)
            {
                var orderItem = ctx.OrderItems.GetById(orderItemDto.Id);

                await ((CustomPriceCalculationService)customPriceCalculationService).CheckAndUpdateOrderItemFinalPricesAsync
                    (orderItem, orderItemDto.IsMeasurable, orderItemDto.NetWeight);
            }
        }

        return await ctx.OrderItemPallets.GetByIdAsync(orderItemPallet.Id, false);
    }

    //[SignalR(SignalRTags.AddOrderItemPallet)]
    //public async Task<OrderItemPallet> AddOrderItemPallet(OrderItemPallet orderItemPallet)
    //{
    //    ArgumentNullException.ThrowIfNull(orderItemPallet);

    //    _logger.Detail($"AddOrderItemPallet invoked; {orderItemPallet}");

    //    if (!await ctx.AddOrderItemPalletSafeAsync(orderItemPallet)) return null;
    //    return await ctx.OrderItemPallets.GetByIdAsync(orderItemPallet.Id, false);
    //}

    //[SignalR(SignalRTags.UpdateOrderItemPallet)]
    //public async Task<OrderItemPallet> UpdateOrderItemPallet(OrderItemPallet orderItemPallet)
    //{
    //    ArgumentNullException.ThrowIfNull(orderItemPallet);

    //    _logger.Detail($"UpdateOrderItemPallet invoked; {orderItemPallet}");

    //    if (!await ctx.UpdateOrderItemPalletSafeAsync(orderItemPallet)) return null;
    //    return await ctx.OrderItemPallets.GetByIdAsync(orderItemPallet.Id, false);
    //}

    //[SignalR(SignalRTags.AddOrUpdateMeasuredOrderItemPallets)]
    //public async Task<ShippingItem> AddOrUpdateMeasuredOrderItemPallets(List<OrderItemPallet> orderItemPallets)
    //{
    //    //    ArgumentNullException.ThrowIfNull(orderItemPallets);

    //    //    _logger.Detail($"AddOrUpdateMeasuredOrderItemPallets invoked; count: {orderItemPallets.Count}");

    //    //    if (orderItemPallets.Count == 0) return null;

    //    //    var shippingItemId = orderItemPallets.FirstOrDefault()!.ShippingItemId;
    //    //    if (shippingItemId <= 0 || orderItemPallets.Any(x => x.ShippingItemId != shippingItemId)) return null;

    //    //    var shippingItem  = await ctx.ShippingItems.GetByIdAsync(shippingItemId, false);
    //    //    shippingItem.orderItemPallets = orderItemPallets.Where(sip => sip.IsValidMeasuringValues(shippingItem.IsMeasurable)).ToList();

    //    //    return await UpdateMeasuredShippingItem(shippingItem);

    //    return null;
    //}

    //[SignalR(SignalRTags.AddOrUpdateMeasuredOrderItemPallet)]
    //public async Task<OrderItemPallet> AddOrUpdateMeasuredOrderItemPallet(OrderItemPallet orderItemPallet)
    //{
    //    ArgumentNullException.ThrowIfNull(orderItemPallet);

    //    _logger.Detail($"AddOrUpdateMeasuredOrderItemPallet invoked; {orderItemPallet}");

    //    if (!await ctx.AddOrUpdateOrderItemPalletSafeAsync(orderItemPallet)) return null;
    //    return await ctx.OrderItemPallets.GetByIdAsync(orderItemPallet.Id, false);
    //}
}