// EN: Handler for DeductInventoryCommand - performs bulk deduction with idempotency.
// VI: Handler cho DeductInventoryCommand - thực hiện trừ kho hàng loạt với idempotency.

using InventoryService.Domain.AggregatesModel.InventoryAggregate;
using InventoryService.Infrastructure.Idempotency;
using MediatR;
using Microsoft.Extensions.Logging;

namespace InventoryService.API.Application.Commands.DeductInventory;

/// <summary>
/// EN: Handles bulk inventory deduction for kitchen ticket served events.
/// VI: Xử lý trừ kho hàng loạt cho sự kiện phiếu bếp đã phục vụ.
/// </summary>
public class DeductInventoryCommandHandler : IRequestHandler<DeductInventoryCommand, DeductInventoryResult>
{
    private readonly IInventoryRepository _repository;
    private readonly IRequestManager _requestManager;
    private readonly ILogger<DeductInventoryCommandHandler> _logger;

    public DeductInventoryCommandHandler(
        IInventoryRepository repository,
        IRequestManager requestManager,
        ILogger<DeductInventoryCommandHandler> logger)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
        _requestManager = requestManager ?? throw new ArgumentNullException(nameof(requestManager));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task<DeductInventoryResult> Handle(DeductInventoryCommand request, CancellationToken cancellationToken)
    {
        // EN: Idempotency check - don't deduct twice for the same reference.
        // VI: Kiểm tra idempotency - không trừ hai lần cho cùng một reference.
        if (await _requestManager.ExistAsync(request.ReferenceId))
        {
            _logger.LogWarning(
                "EN: Duplicate deduction request detected, skipping / VI: Phát hiện yêu cầu trừ kho trùng lặp, bỏ qua - ReferenceId: {ReferenceId}",
                request.ReferenceId);

            return new DeductInventoryResult(
                Success: true,
                ItemsDeducted: 0,
                ItemsSkipped: request.Items.Count,
                Items: request.Items.Select(i => new DeductionResultItem(
                    i.InventoryItemId, i.IngredientName, false, "Duplicate request")).ToList());
        }

        // EN: Record this request for idempotency.
        // VI: Ghi nhận request này cho idempotency.
        await _requestManager.CreateRequestForCommandAsync<DeductInventoryCommand>(request.ReferenceId);

        var resultItems = new List<DeductionResultItem>();
        var itemsDeducted = 0;
        var itemsSkipped = 0;

        foreach (var deductionItem in request.Items)
        {
            try
            {
                var inventoryItem = await _repository.GetByIdAsync(deductionItem.InventoryItemId, cancellationToken);

                if (inventoryItem == null)
                {
                    _logger.LogWarning(
                        "EN: Inventory item not found for deduction / VI: Không tìm thấy inventory item để trừ - ItemId: {ItemId}, Ingredient: {Ingredient}",
                        deductionItem.InventoryItemId, deductionItem.IngredientName);

                    resultItems.Add(new DeductionResultItem(
                        deductionItem.InventoryItemId, deductionItem.IngredientName, false, "Item not found"));
                    itemsSkipped++;
                    continue;
                }

                // EN: Check if sufficient stock is available.
                // VI: Kiểm tra xem có đủ stock hay không.
                if (inventoryItem.AvailableQuantity < deductionItem.Amount)
                {
                    _logger.LogWarning(
                        "EN: Insufficient stock for deduction / VI: Không đủ stock để trừ - ItemId: {ItemId}, Available: {Available}, Requested: {Requested}",
                        deductionItem.InventoryItemId, inventoryItem.AvailableQuantity, deductionItem.Amount);

                    // EN: Deduct what's available instead of failing entirely.
                    // VI: Trừ những gì có sẵn thay vì thất bại hoàn toàn.
                    if (inventoryItem.AvailableQuantity > 0)
                    {
                        inventoryItem.StockOut(
                            inventoryItem.AvailableQuantity,
                            $"[Partial] {request.Reason} (requested: {deductionItem.Amount}, available: {inventoryItem.AvailableQuantity})",
                            request.ReferenceId);

                        resultItems.Add(new DeductionResultItem(
                            deductionItem.InventoryItemId, deductionItem.IngredientName, true,
                            $"Partial deduction: {inventoryItem.AvailableQuantity} of {deductionItem.Amount}"));
                        itemsDeducted++;
                    }
                    else
                    {
                        resultItems.Add(new DeductionResultItem(
                            deductionItem.InventoryItemId, deductionItem.IngredientName, false, "Insufficient stock (0 available)"));
                        itemsSkipped++;
                    }
                    continue;
                }

                // EN: Perform the deduction.
                // VI: Thực hiện trừ kho.
                inventoryItem.StockOut(deductionItem.Amount, request.Reason, request.ReferenceId);

                resultItems.Add(new DeductionResultItem(
                    deductionItem.InventoryItemId, deductionItem.IngredientName, true, null));
                itemsDeducted++;

                _logger.LogInformation(
                    "EN: Deducted inventory / VI: Đã trừ kho - ItemId: {ItemId}, Amount: {Amount}, Ingredient: {Ingredient}",
                    deductionItem.InventoryItemId, deductionItem.Amount, deductionItem.IngredientName);
            }
            catch (Exception ex)
            {
                // EN: Log error for individual item but continue with remaining items.
                // VI: Log lỗi cho item riêng lẻ nhưng tiếp tục với các items còn lại.
                _logger.LogError(ex,
                    "EN: Error deducting inventory item / VI: Lỗi trừ inventory item - ItemId: {ItemId}",
                    deductionItem.InventoryItemId);

                resultItems.Add(new DeductionResultItem(
                    deductionItem.InventoryItemId, deductionItem.IngredientName, false, ex.Message));
                itemsSkipped++;
            }
        }

        // EN: Save all changes in one transaction.
        // VI: Lưu tất cả thay đổi trong một transaction.
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation(
            "EN: Bulk inventory deduction completed / VI: Trừ kho hàng loạt hoàn thành - ReferenceId: {ReferenceId}, Deducted: {Deducted}, Skipped: {Skipped}",
            request.ReferenceId, itemsDeducted, itemsSkipped);

        return new DeductInventoryResult(
            Success: itemsDeducted > 0,
            ItemsDeducted: itemsDeducted,
            ItemsSkipped: itemsSkipped,
            Items: resultItems);
    }
}
