using Dapper;
using FastEndpoints;
using Npgsql;

namespace Modules.ERP.Inventory.Search;

public sealed record SearchInventoryRequest(string? Keyword, int Page = 1, int PageSize = 200);
public sealed record InventoryItemRow(Guid ItemId, string ItemCode, string ItemName, string Specification, decimal TotalQty, decimal AvailableQty, decimal AllocatedQty, decimal HoldQty);
public sealed record SearchInventoryResponse(IReadOnlyList<InventoryItemRow> Items, int TotalCount);

public sealed class Endpoint(NpgsqlDataSource dataSource) : Endpoint<SearchInventoryRequest, SearchInventoryResponse>
{
    public override void Configure()
    {
        Get("/api/erp/inventory");
        Permissions("erp.inventory.read");
    }

    public override async Task HandleAsync(SearchInventoryRequest req, CancellationToken ct)
    {
        var page = Math.Max(req.Page, 1);
        var pageSize = Math.Clamp(req.PageSize, 1, 500);
        var keyword = string.IsNullOrWhiteSpace(req.Keyword) ? null : req.Keyword.Trim();
        var offset = (page - 1) * pageSize;
        const string sql = """
        select item_id as ItemId, item_code as ItemCode, item_name as ItemName, specification,
               sum(on_hand_qty) as TotalQty,
               sum(available_qty) as AvailableQty,
               sum(allocated_qty) as AllocatedQty,
               sum(hold_qty) as HoldQty
        from erp_inventory_snapshot_projection
        where (@Keyword is null or search_text ilike '%' || @Keyword || '%')
        group by item_id, item_code, item_name, specification
        order by item_code
        limit @PageSize offset @Offset;

        select count(distinct item_id)::int
        from erp_inventory_snapshot_projection
        where (@Keyword is null or search_text ilike '%' || @Keyword || '%');
        """;
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        using var multi = await connection.QueryMultipleAsync(new CommandDefinition(sql, new { Keyword = keyword, PageSize = pageSize, Offset = offset }, cancellationToken: ct));
        var items = (await multi.ReadAsync<InventoryItemRow>()).AsList();
        var totalCount = await multi.ReadSingleAsync<int>();
        await Send.OkAsync(new SearchInventoryResponse(items, totalCount), ct);
    }
}
