#nullable enable
using System.Threading.Tasks;
using AyCode.Core.Loggers;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using Mango.Nop.Core.Entities;
using Mango.Nop.Core.Loggers;
using Mango.Nop.Data.Repositories;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Orders;
using Nop.Core.Events;
using Nop.Data;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Catalog;

namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;

public class StockTakingDbContext : MgDbContextBase,
    IStockTakingDbSet<StockTakingDbTable>,
    IOrderItemDtoDbSet<OrderItemDtoDbTable>,
    IStockTakingItemDbSet<StockTakingItemDbTable>,
    IStockTakingItemPalletDbSet<StockTakingItemPalletDbTable>
{
    private FruitBankDbContext _fruitBankDbContext;
    public ProductDtoDbTable ProductDtos { get; set; }
    public OrderItemDtoDbTable OrderItemDtos { get; set; }

    public StockTakingDbTable StockTakings { get; set; }
    public StockTakingItemDbTable StockTakingItems { get; set; }
    public StockTakingItemPalletDbTable StockTakingItemPallets { get; set; }
    public StockQuantityHistoryDtoDbTable StockQuantityHistoryDtos { get; set; }

    public IRepository<GenericAttribute> GenericAttributes { get; set; }
    public IRepository<StockQuantityHistory> StockQuantityHistories { get; set; }
    public IRepository<StockQuantityHistoryExt> StockQuantityHistoriesExt { get; set; }

    private readonly IStoreContext _storeContext;
    private readonly IProductService _productService;
    private readonly IStaticCacheManager _staticCacheManager;
    protected readonly IEventPublisher _eventPublisher;

    public StockTakingDbContext(INopDataProvider dataProvider, ILockService lockService, IStoreContext storeContext, FruitBankDbContext fruitBankDbContext,
        ProductDtoDbTable productDtoDbTable,
        OrderItemDtoDbTable orderItemDtoDbTable,
        StockQuantityHistoryDtoDbTable stockQuantityHistoryDtos,
        StockTakingDbTable stockTakingDbTable,
        StockTakingItemDbTable stockTakingItemDbTable,
        StockTakingItemPalletDbTable stockTakingItemPalletDbTable,
        IProductService productService, IStaticCacheManager staticCacheManager,
        IRepository<Order> orderRepository,
        IRepository<OrderItem> orderItemRepository,
        IRepository<Product> productRepository,
        IRepository<GenericAttribute> genericAttributes,
        IRepository<StockQuantityHistory> stockQuantityHistories,
        IRepository<StockQuantityHistoryExt> stockQuantityHistoriesExt,
        IEventPublisher eventPublisher,
        IEnumerable<IAcLogWriterBase> logWriters) : base(productRepository, orderRepository, orderItemRepository, dataProvider, lockService, new Logger<StockTakingDbContext>(logWriters.ToArray()))
    {
        _eventPublisher = eventPublisher;
        _storeContext = storeContext;
        _productService = productService;
        _staticCacheManager = staticCacheManager;

        _fruitBankDbContext = fruitBankDbContext;

        ProductDtos = productDtoDbTable;
        OrderItemDtos = orderItemDtoDbTable;

        GenericAttributes = genericAttributes;

        StockQuantityHistories = stockQuantityHistories;
        StockQuantityHistoriesExt = stockQuantityHistoriesExt;
        StockQuantityHistoryDtos = stockQuantityHistoryDtos;

        StockTakings = stockTakingDbTable;
        StockTakingItems = stockTakingItemDbTable;
        StockTakingItemPallets = stockTakingItemPalletDbTable;
    }

    public async Task CloseStockTaking(int stockTakingId)
    {
        Logger.Info($"CloseStockTaking invoked! stockTakingId: {stockTakingId}");

        var count = 0;

        var stockTaking = await StockTakings.GetByIdAsync(stockTakingId, true);

        if (!stockTaking.IsReadyForClose()) throw new Exception($"Not all IsRequiredForMeasuring items are IsMeasured! IsReadyForClose: false;");

        //TODO: Mi legyen az invalid item-ekkel? - J.
        foreach (var stockTakingItem in stockTaking.StockTakingItems!.Where(stockTakingItem => stockTakingItem is { IsMeasured: true, IsInvalid: false }))
        {
            count++;

            await _fruitBankDbContext.UpdateStockQuantityAndWeightAsync(stockTakingItem.Product!, stockTakingItem.QuantityDiff,
                $"Leltár által módosítva! stockTakingId: #{stockTaking.Id}, stockTakingItemId: #{stockTakingItem.Id}",
                stockTakingItem.NetWeightDiff);

            Logger.Debug($"CloseStockTaking.UpdateStockQuantityAndWeightAsync; {stockTakingItem.Product}; qntDiff: {stockTakingItem.QuantityDiff} db; netWeightDiff: {stockTakingItem.NetWeightDiff} kg");
        }

        stockTaking.IsClosed = true;
        await StockTakings.UpdateAsync(stockTaking);

        Logger.Info($"StockTaking closed! stockTakingId: {stockTaking.Id}; stockTakingItems count: {count}");
    }

    public async Task<StockTakingItemPallet> AddOrUpdateMeasuredStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet)
    {
        if (stockTakingItemPallet.Id == 0) return await AddStockTakingItemPallet(stockTakingItemPallet);
        return await UpdateStockTakingItemPallet(stockTakingItemPallet);
    }

    public async Task<StockTakingItemPallet> AddStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet)
    {
        await TransactionSafeAsync(async _ =>
        {
            await StockTakingItemPallets.InsertAsync(stockTakingItemPallet);
            await RefreshStockTakingItemMeasuredValuesFromPallets(stockTakingItemPallet.StockTakingItemId);

            return true;
        });

        return await StockTakingItemPallets.GetByIdAsync(stockTakingItemPallet.Id);
    }

    public async Task<StockTakingItemPallet> UpdateStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet)
    {
        await TransactionSafeAsync(async _ =>
        {
            await StockTakingItemPallets.UpdateAsync(stockTakingItemPallet);
            await RefreshStockTakingItemMeasuredValuesFromPallets(stockTakingItemPallet.StockTakingItemId);

            return true;
        });

        return await StockTakingItemPallets.GetByIdAsync(stockTakingItemPallet.Id);
    }

    private async Task RefreshStockTakingItemMeasuredValuesFromPallets(int stockTakingItemId)
    {
        var stockTakingItem = await StockTakingItems.GetByIdAsync(stockTakingItemId, true)!;

        if (stockTakingItem.IsInvalid) throw new Exception($"stockTakingItem.IsInvalid");
        if (stockTakingItem.StockTaking!.IsClosed) throw new Exception($"stockTakingItem.StockTaking.IsClosed");
        if (stockTakingItem.StockTakingItemPallets!.Count == 0) throw new Exception($"stockTakingItem.StockTakingItemPallets.Count == 0");
        if (stockTakingItem.StockTakingItemPallets!.Any(x => !x.IsValidMeasuringValues(stockTakingItem.IsMeasurable))) throw new Exception($"IsValidMeasuringValues == false");

        
        stockTakingItem.IsMeasured = true;
        stockTakingItem.MeasuredStockQuantity = stockTakingItem.StockTakingItemPallets.Sum(x => x.TrayQuantity);

        if (stockTakingItem.IsMeasurable) stockTakingItem.MeasuredNetWeight = double.Round(stockTakingItem.StockTakingItemPallets.Sum(x => x.NetWeight), 1);

        await StockTakingItems.UpdateAsync(stockTakingItem);
    }
}