using Dapper;
using Npgsql;

namespace Modules.WMS.Picking.Shared;

public static class PickingTaskQueries
{
    public static async Task<PickingTaskDto?> GetAsync(
        NpgsqlConnection connection,
        NpgsqlTransaction? transaction,
        Guid taskId,
        CancellationToken ct)
    {
        const string taskSql = """
            select id as TaskId,
                   task_no as TaskNo,
                   status as Status,
                   version as Version
              from wms.picking_tasks
             where id = @TaskId;
            """;

        var task = await connection.QuerySingleOrDefaultAsync<TaskRow>(
            new CommandDefinition(taskSql, new { TaskId = taskId }, transaction, cancellationToken: ct));
        if (task is null) return null;

        const string linesSql = """
            select l.id as LineId,
                   l.line_no as LineNo,
                   l.location_code as LocationCode,
                   l.item_id as ItemId,
                   i.code as ItemCode,
                   i.name as ItemName,
                   l.item_option as ItemOption,
                   l.barcode as Barcode,
                   l.required_qty as RequiredQty,
                   l.picked_qty as PickedQty,
                   greatest(l.required_qty - l.picked_qty, 0) as RemainingQty,
                   l.status as Status,
                   l.location_confirmed as LocationConfirmed
              from wms.picking_lines l
              join catalog.items i on i.id = l.item_id
             where l.task_id = @TaskId
             order by l.line_no;
            """;

        var lines = (await connection.QueryAsync<LineRow>(
            new CommandDefinition(linesSql, new { TaskId = taskId }, transaction, cancellationToken: ct))).AsList();

        var current = lines.FirstOrDefault(x => x.Status != "COMPLETED");
        var completedLines = lines.Count(x => x.Status == "COMPLETED");
        var totalQty = lines.Sum(x => x.RequiredQty);
        var completedQty = lines.Sum(x => x.PickedQty);

        var stage = task.Status switch
        {
            "READY" => "ready",
            "COMPLETED" => "completed",
            "BLOCKED" => "blocked",
            _ when current is null => "completed",
            _ when current.LocationConfirmed => "await-item",
            _ => "await-location"
        };

        return new PickingTaskDto(
            task.TaskId,
            task.TaskNo,
            stage,
            task.Status,
            completedLines,
            lines.Count,
            completedQty,
            totalQty,
            task.Version,
            current is null ? null : new PickingLineDto(
                current.LineId,
                current.LineNo,
                current.LocationCode,
                current.ItemId,
                current.ItemCode,
                current.ItemName,
                current.ItemOption,
                current.Barcode,
                current.RequiredQty,
                current.PickedQty,
                current.RemainingQty));
    }

    private sealed record TaskRow(Guid TaskId, string TaskNo, string Status, long Version);
    private sealed record LineRow(
        Guid LineId,
        int LineNo,
        string LocationCode,
        Guid ItemId,
        string ItemCode,
        string ItemName,
        string? ItemOption,
        string Barcode,
        decimal RequiredQty,
        decimal PickedQty,
        decimal RemainingQty,
        string Status,
        bool LocationConfirmed = false);
}
