// EN: Command handlers for Inventory Service.
// VI: Command handlers cho Inventory Service.

using InventoryService.Domain.AggregatesModel.InventoryAggregate;
using InventoryService.Domain.SeedWork;
using MediatR;
using Microsoft.Extensions.Logging;

namespace InventoryService.API.Application.Commands;

/// <summary>
/// EN: Handler for CreateInventoryItemCommand.
/// VI: Handler cho CreateInventoryItemCommand.
/// </summary>
public class CreateInventoryItemCommandHandler : IRequestHandler<CreateInventoryItemCommand, Guid>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<CreateInventoryItemCommandHandler> _logger;

    public CreateInventoryItemCommandHandler(
        IInventoryRepository repository,
        ILogger<CreateInventoryItemCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<Guid> Handle(CreateInventoryItemCommand request, CancellationToken ct)
    {
        // EN: Resolve ItemType from ID
        // VI: Xác định ItemType từ ID
        var itemType = Enumeration.FromValue<ItemType>(request.ItemTypeId);

        var item = new InventoryItem(
            request.ShopId,
            request.Name,
            itemType,
            request.Unit,
            request.CostPerUnit,
            request.InitialQuantity,
            request.ReorderLevel,
            request.SupplierName,
            request.ExpiryDate);

        await _repository.AddAsync(item, ct);
        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Inventory item created / VI: Inventory item đã tạo - Name: {Name}, Shop: {ShopId}, Type: {ItemType}",
            request.Name, request.ShopId, itemType.Name);

        return item.Id;
    }
}

/// <summary>
/// EN: Handler for StockInCommand.
/// VI: Handler cho StockInCommand.
/// </summary>
public class StockInCommandHandler : IRequestHandler<StockInCommand, Guid>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<StockInCommandHandler> _logger;

    public StockInCommandHandler(
        IInventoryRepository repository,
        ILogger<StockInCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<Guid> Handle(StockInCommand request, CancellationToken ct)
    {
        // EN: Get or create inventory item
        // VI: Lấy hoặc tạo inventory item
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId,
            request.ShopId,
            ct);

        if (item == null)
        {
            item = new InventoryItem(request.ProductId, request.ShopId);
            await _repository.AddAsync(item, ct);
        }

        // EN: Perform stock in operation with invoice and unit cost
        // VI: Thực hiện nhập kho với hóa đơn và giá đơn vị
        item.StockIn(request.Amount, request.Notes, request.ReferenceId, request.InvoiceImageUrl, request.UnitCost);

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stock in completed / VI: Nhập kho hoàn thành - Product: {ProductId}, Shop: {ShopId}, Amount: {Amount}",
            request.ProductId, request.ShopId, request.Amount);

        return item.Id;
    }
}

/// <summary>
/// EN: Handler for StockOutCommand.
/// VI: Handler cho StockOutCommand.
/// </summary>
public class StockOutCommandHandler : IRequestHandler<StockOutCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<StockOutCommandHandler> _logger;

    public StockOutCommandHandler(
        IInventoryRepository repository,
        ILogger<StockOutCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(StockOutCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId, 
            request.ShopId, 
            ct);

        if (item == null)
            return false;

        item.StockOut(request.Amount, request.Notes, request.ReferenceId);

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stock out completed / VI: Xuất kho hoàn thành - Product: {ProductId}, Shop: {ShopId}, Amount: {Amount}",
            request.ProductId, request.ShopId, request.Amount);

        return true;
    }
}

/// <summary>
/// EN: Handler for ReserveStockCommand.
/// VI: Handler cho ReserveStockCommand.
/// </summary>
public class ReserveStockCommandHandler : IRequestHandler<ReserveStockCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<ReserveStockCommandHandler> _logger;

    public ReserveStockCommandHandler(
        IInventoryRepository repository,
        ILogger<ReserveStockCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(ReserveStockCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId,
            request.ShopId,
            ct);

        if (item == null)
            return false;

        item.Reserve(request.Amount, request.OrderId);

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stock reserved / VI: Stock đã đặt trước - Product: {ProductId}, Shop: {ShopId}, Amount: {Amount}, Order: {OrderId}",
            request.ProductId, request.ShopId, request.Amount, request.OrderId);

        return true;
    }
}

/// <summary>
/// EN: Handler for ReleaseReservationCommand.
/// VI: Handler cho ReleaseReservationCommand.
/// </summary>
public class ReleaseReservationCommandHandler : IRequestHandler<ReleaseReservationCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<ReleaseReservationCommandHandler> _logger;

    public ReleaseReservationCommandHandler(
        IInventoryRepository repository,
        ILogger<ReleaseReservationCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(ReleaseReservationCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId,
            request.ShopId,
            ct);

        if (item == null)
            return false;

        item.ReleaseReservation(request.Amount, request.OrderId);

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Reservation released / VI: Đặt trước đã giải phóng - Product: {ProductId}, Shop: {ShopId}, Amount: {Amount}, Order: {OrderId}",
            request.ProductId, request.ShopId, request.Amount, request.OrderId);

        return true;
    }
}

/// <summary>
/// EN: Handler for AdjustStockCommand.
/// VI: Handler cho AdjustStockCommand.
/// </summary>
public class AdjustStockCommandHandler : IRequestHandler<AdjustStockCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<AdjustStockCommandHandler> _logger;

    public AdjustStockCommandHandler(
        IInventoryRepository repository,
        ILogger<AdjustStockCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(AdjustStockCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId,
            request.ShopId,
            ct);

        if (item == null)
            return false;

        item.Adjust(request.NewQuantity, request.Notes);

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stock adjusted / VI: Stock đã điều chỉnh - Product: {ProductId}, Shop: {ShopId}, NewQuantity: {NewQuantity}",
            request.ProductId, request.ShopId, request.NewQuantity);

        return true;
    }
}

/// <summary>
/// EN: Handler for StockOutByIdCommand - deducts by inventory item ID directly.
/// VI: Handler cho StockOutByIdCommand - trừ kho theo inventory item ID trực tiếp.
/// </summary>
public class StockOutByIdCommandHandler : IRequestHandler<StockOutByIdCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<StockOutByIdCommandHandler> _logger;

    public StockOutByIdCommandHandler(
        IInventoryRepository repository,
        ILogger<StockOutByIdCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(StockOutByIdCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByIdAsync(request.InventoryItemId, ct);
        if (item == null) return false;

        item.StockOut(request.Amount, request.Notes, request.ReferenceId);
        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stock out by ID completed / VI: Xuất kho theo ID hoàn thành - ItemId: {ItemId}, Amount: {Amount}",
            request.InventoryItemId, request.Amount);

        return true;
    }
}

/// <summary>
/// EN: Handler for RecordWastageCommand.
/// VI: Handler cho RecordWastageCommand.
/// </summary>
public class RecordWastageCommandHandler : IRequestHandler<RecordWastageCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<RecordWastageCommandHandler> _logger;

    public RecordWastageCommandHandler(
        IInventoryRepository repository,
        ILogger<RecordWastageCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(RecordWastageCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByIdAsync(request.InventoryItemId, ct);
        if (item == null) return false;

        item.RecordWastage(request.Amount, request.Reason, request.Notes);
        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Wastage recorded / VI: Hao hụt đã ghi nhận - ItemId: {ItemId}, Amount: {Amount}, Reason: {Reason}",
            request.InventoryItemId, request.Amount, request.Reason);

        return true;
    }
}

/// <summary>
/// EN: Handler for StocktakeCommand.
/// VI: Handler cho StocktakeCommand.
/// </summary>
public class StocktakeCommandHandler : IRequestHandler<StocktakeCommand, StocktakeResult>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<StocktakeCommandHandler> _logger;

    public StocktakeCommandHandler(
        IInventoryRepository repository,
        ILogger<StocktakeCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<StocktakeResult> Handle(StocktakeCommand request, CancellationToken ct)
    {
        var discrepancies = new List<StocktakeDiscrepancy>();

        foreach (var stocktakeItem in request.Items)
        {
            var item = await _repository.GetByIdAsync(stocktakeItem.InventoryItemId, ct);
            if (item == null) continue;

            var expectedQty = item.Quantity;
            var diff = stocktakeItem.CountedQuantity - expectedQty;

            if (diff != 0)
            {
                discrepancies.Add(new StocktakeDiscrepancy(
                    item.Id, item.Name, expectedQty, stocktakeItem.CountedQuantity, diff));
            }

            item.Adjust(stocktakeItem.CountedQuantity, "Stocktake");
        }

        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Stocktake completed / VI: Kiểm kê hoàn thành - Shop: {ShopId}, Items: {Count}, Discrepancies: {Discrepancies}",
            request.ShopId, request.Items.Count, discrepancies.Count);

        return new StocktakeResult(discrepancies, request.Items.Count);
    }
}

/// <summary>
/// EN: Handler for DeleteInventoryItemCommand.
/// VI: Handler cho DeleteInventoryItemCommand.
/// </summary>
public class DeleteInventoryItemCommandHandler : IRequestHandler<DeleteInventoryItemCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<DeleteInventoryItemCommandHandler> _logger;

    public DeleteInventoryItemCommandHandler(
        IInventoryRepository repository,
        ILogger<DeleteInventoryItemCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(DeleteInventoryItemCommand request, CancellationToken cancellationToken)
    {
        var item = await _repository.GetByIdAsync(request.InventoryItemId);
        if (item == null) return false;

        _repository.Delete(item);
        await _repository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
        _logger.LogInformation("EN: Inventory item deleted / VI: Đã xóa nguyên liệu: {Id}", request.InventoryItemId);
        return true;
    }
}

/// <summary>
/// EN: Handler for SetLowStockAlertCommand — sets the reorder level (low stock threshold).
/// VI: Handler cho SetLowStockAlertCommand — cài đặt mức đặt hàng lại (ngưỡng stock thấp).
/// </summary>
public class SetLowStockAlertCommandHandler : IRequestHandler<SetLowStockAlertCommand, bool>
{
    private readonly IInventoryRepository _repository;
    private readonly ILogger<SetLowStockAlertCommandHandler> _logger;

    public SetLowStockAlertCommandHandler(
        IInventoryRepository repository,
        ILogger<SetLowStockAlertCommandHandler> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public async Task<bool> Handle(SetLowStockAlertCommand request, CancellationToken ct)
    {
        var item = await _repository.GetByProductAndShopAsync(
            request.ProductId, request.ShopId, ct);

        if (item == null)
            return false;

        item.SetReorderLevel(request.MinimumLevel);
        await _repository.UnitOfWork.SaveChangesAsync(ct);

        _logger.LogInformation(
            "EN: Low stock alert set / VI: Cảnh báo stock thấp đã cài đặt - Product: {ProductId}, Shop: {ShopId}, MinLevel: {MinLevel}",
            request.ProductId, request.ShopId, request.MinimumLevel);

        return true;
    }
}
