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

using InventoryService.API.Application.DTOs;
using InventoryService.API.Application.Mappers;
using InventoryService.Domain.AggregatesModel.InventoryAggregate;
using MediatR;

namespace InventoryService.API.Application.Queries;

/// <summary>
/// EN: Handler for GetInventoryByShopQuery.
/// VI: Handler cho GetInventoryByShopQuery.
/// </summary>
public class GetInventoryByShopQueryHandler 
    : IRequestHandler<GetInventoryByShopQuery, PagedResult<InventoryItemDto>>
{
    private readonly IInventoryRepository _repository;

    public GetInventoryByShopQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

    public async Task<PagedResult<InventoryItemDto>> Handle(
        GetInventoryByShopQuery request, 
        CancellationToken ct)
    {
        var (items, total) = await _repository.GetByShopAsync(
            request.ShopId, 
            request.Skip, 
            request.Take, 
            ct);

        var dtos = items.Select(i => i.ToDto()).ToList();

        return new PagedResult<InventoryItemDto>(dtos, total);
    }
}

/// <summary>
/// EN: Handler for GetStockLevelQuery.
/// VI: Handler cho GetStockLevelQuery.
/// </summary>
public class GetStockLevelQueryHandler 
    : IRequestHandler<GetStockLevelQuery, InventoryItemDto?>
{
    private readonly IInventoryRepository _repository;

    public GetStockLevelQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

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

        return item?.ToDto();
    }
}

/// <summary>
/// EN: Handler for GetTransactionsQuery.
/// VI: Handler cho GetTransactionsQuery.
/// </summary>
public class GetTransactionsQueryHandler 
    : IRequestHandler<GetTransactionsQuery, PagedResult<InventoryTransactionDto>>
{
    private readonly IInventoryRepository _repository;

    public GetTransactionsQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

    public async Task<PagedResult<InventoryTransactionDto>> Handle(
        GetTransactionsQuery request, 
        CancellationToken ct)
    {
        var (transactions, total) = await _repository.GetTransactionsAsync(
            request.InventoryItemId, 
            request.Skip, 
            request.Take, 
            ct);

        var dtos = transactions.Select(t => t.ToDto()).ToList();

        return new PagedResult<InventoryTransactionDto>(dtos, total);
    }
}

/// <summary>
/// EN: Handler for GetTransactionsByShopQuery.
/// VI: Handler cho GetTransactionsByShopQuery.
/// </summary>
public class GetTransactionsByShopQueryHandler
    : IRequestHandler<GetTransactionsByShopQuery, PagedResult<InventoryTransactionDto>>
{
    private readonly IInventoryRepository _repository;

    public GetTransactionsByShopQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

    public async Task<PagedResult<InventoryTransactionDto>> Handle(
        GetTransactionsByShopQuery request,
        CancellationToken ct)
    {
        var (transactions, total) = await _repository.GetTransactionsByShopAsync(
            request.ShopId,
            request.Skip,
            request.Take,
            ct);

        var dtos = transactions.Select(t => t.ToDto()).ToList();

        return new PagedResult<InventoryTransactionDto>(dtos, total);
    }
}

/// <summary>
/// EN: Handler for GetStockLevelsQuery (bulk stock check for POS cart validation).
/// VI: Handler cho GetStockLevelsQuery (kiểm tra stock hàng loạt cho xác thực giỏ hàng POS).
/// </summary>
public class GetStockLevelsQueryHandler
    : IRequestHandler<GetStockLevelsQuery, IReadOnlyList<StockLevelDto>>
{
    private readonly IInventoryRepository _repository;

    public GetStockLevelsQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

    public async Task<IReadOnlyList<StockLevelDto>> Handle(
        GetStockLevelsQuery request,
        CancellationToken ct)
    {
        var items = await _repository.GetByProductIdsAndShopAsync(
            request.ShopId, request.ProductIds, ct);

        // EN: Map inventory items to StockLevelDto; include entries for missing products.
        // VI: Map inventory items sang StockLevelDto; bao gồm entries cho các product thiếu.
        var itemsByProduct = items.ToDictionary(i => i.ProductId);

        return request.ProductIds.Select(pid =>
        {
            if (itemsByProduct.TryGetValue(pid, out var item))
            {
                return new StockLevelDto(
                    pid,
                    item.AvailableQuantity,
                    item.ReservedQuantity,
                    item.ReorderLevel,
                    item.AvailableQuantity <= item.ReorderLevel);
            }

            // EN: Product has no inventory record — report as zero stock.
            // VI: Product không có inventory record — báo cáo stock bằng 0.
            return new StockLevelDto(pid, 0, 0, 0, true);
        }).ToList();
    }
}

/// <summary>
/// EN: Handler for GetLowStockItemsQuery.
/// VI: Handler cho GetLowStockItemsQuery.
/// </summary>
public class GetLowStockItemsQueryHandler 
    : IRequestHandler<GetLowStockItemsQuery, PagedResult<InventoryItemDto>>
{
    private readonly IInventoryRepository _repository;

    public GetLowStockItemsQueryHandler(IInventoryRepository repository)
    {
        _repository = repository;
    }

    public async Task<PagedResult<InventoryItemDto>> Handle(
        GetLowStockItemsQuery request, 
        CancellationToken ct)
    {
        var (items, total) = await _repository.GetLowStockItemsAsync(
            request.ShopId, 
            request.Skip, 
            request.Take, 
            ct);

        var dtos = items.Select(i => i.ToDto()).ToList();

        return new PagedResult<InventoryItemDto>(dtos, total);
    }
}
