using CMSMicroservice.Application.Common.Exceptions;
using CMSMicroservice.Domain.Entities;
using CMSMicroservice.Domain.Enums;

namespace CMSMicroservice.Application.StockMovementCQ.Commands.CreateStockMovement;

public class CreateStockMovementCommandHandler : IRequestHandler<CreateStockMovementCommand, CreateStockMovementResponseDto>
{
    private readonly IApplicationDbContext _context;

    public CreateStockMovementCommandHandler(IApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<CreateStockMovementResponseDto> Handle(CreateStockMovementCommand request, CancellationToken cancellationToken)
    {
        var inventoryItem = await _context.InventoryItems
            .FirstOrDefaultAsync(i => i.Id == request.InventoryItemId, cancellationToken);

        if (inventoryItem == null)
        {
            throw new NotFoundException(nameof(InventoryItem), request.InventoryItemId);
        }

        var quantityBefore = inventoryItem.Quantity;
        var quantityAfter = quantityBefore;

        // اعمال تغییرات موجودی بر اساس نوع حرکت
        switch (request.MovementType)
        {
            case StockMovementType.InitialStock:
            case StockMovementType.Restock:
            case StockMovementType.Return:
            case StockMovementType.AdjustmentPlus:
            case StockMovementType.TransferIn:
                quantityAfter = quantityBefore + request.Quantity;
                inventoryItem.Quantity = quantityAfter;
                inventoryItem.LastRestockedAt = DateTime.UtcNow;
                break;

            case StockMovementType.Sale:
            case StockMovementType.AdjustmentMinus:
            case StockMovementType.Lost:
            case StockMovementType.Damaged:
            case StockMovementType.TransferOut:
                if (inventoryItem.Quantity < request.Quantity)
                {
                    throw new InvalidOperationException($"Insufficient stock. Available: {inventoryItem.Quantity}, Requested: {request.Quantity}");
                }
                quantityAfter = quantityBefore - request.Quantity;
                inventoryItem.Quantity = quantityAfter;
                if (request.MovementType == StockMovementType.Sale)
                {
                    inventoryItem.LastSoldAt = DateTime.UtcNow;
                }
                break;

            case StockMovementType.Reserved:
                if (inventoryItem.AvailableQuantity < request.Quantity)
                {
                    throw new InvalidOperationException($"Insufficient available stock. Available: {inventoryItem.AvailableQuantity}, Requested: {request.Quantity}");
                }
                // رزرو فقط ReservedQuantity رو زیاد می‌کنه، Quantity فیزیکی تغییر نمی‌کنه
                inventoryItem.ReservedQuantity += request.Quantity;
                // quantityAfter = quantityBefore (بدون تغییر)
                break;

            case StockMovementType.Released:
                if (inventoryItem.ReservedQuantity < request.Quantity)
                {
                    throw new InvalidOperationException($"Cannot release more than reserved. Reserved: {inventoryItem.ReservedQuantity}, Requested: {request.Quantity}");
                }
                // آزادسازی فقط ReservedQuantity رو کم می‌کنه، Quantity فیزیکی تغییر نمی‌کنه
                inventoryItem.ReservedQuantity -= request.Quantity;
                // quantityAfter = quantityBefore (بدون تغییر)
                break;
        }

        // محاسبه مقدار Quantity برای ذخیره (منفی برای عملیات کاهشی)
        // تا constraint دیتابیس (QuantityAfter = QuantityBefore + Quantity) برقرار بماند
        var storedQuantity = request.MovementType switch
        {
            StockMovementType.Sale or
            StockMovementType.AdjustmentMinus or
            StockMovementType.Lost or
            StockMovementType.Damaged or
            StockMovementType.TransferOut => -request.Quantity,
            // رزرو و آزادسازی تأثیری روی Quantity فیزیکی ندارن
            StockMovementType.Reserved or
            StockMovementType.Released => 0,
            _ => request.Quantity
        };

        var entity = new StockMovement
        {
            InventoryItemId = request.InventoryItemId,
            MovementType = request.MovementType,
            Quantity = storedQuantity,
            QuantityBefore = quantityBefore,
            QuantityAfter = quantityAfter,
            Note = request.Note,
            ReferenceNumber = request.ReferenceNumber ?? $"MVT-{DateTime.UtcNow:yyyyMMddHHmmss}",
            OrderId = request.OrderId,
            DiscountOrderId = request.DiscountOrderId,
            PerformedByUserId = request.PerformedByUserId
        };

        await _context.StockMovements.AddAsync(entity, cancellationToken);
        await _context.SaveChangesAsync(cancellationToken);

        return new CreateStockMovementResponseDto { Id = entity.Id };
    }
}
