using Dapper;
using FastEndpoints;
using Modules.WMS.Picking.Shared;
using Npgsql;
using Shared.Problems;

namespace Modules.WMS.Picking.Start;

public sealed record StartPickingRequest(long ExpectedVersion);

public sealed class Endpoint(NpgsqlDataSource dataSource)
    : Endpoint<StartPickingRequest, PickingTaskDto>
{
    public override void Configure()
    {
        Post("/api/wms/picking/tasks/{taskId:guid}/start");
        Permissions("wms.picking.execute");
    }

    public override async Task HandleAsync(StartPickingRequest req, CancellationToken ct)
    {
        var taskId = Route<Guid>("taskId");
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        await using var tx = await connection.BeginTransactionAsync(ct);

        const string sql = """
            update wms.picking_tasks
               set status = 'IN_PROGRESS',
                   started_at = coalesce(started_at, now()),
                   version = version + 1,
                   updated_at = now()
             where id = @TaskId
               and status = 'READY'
               and version = @ExpectedVersion
         returning version;
            """;

        var version = await connection.QuerySingleOrDefaultAsync<long?>(
            new CommandDefinition(sql, new { TaskId = taskId, req.ExpectedVersion }, tx, cancellationToken: ct));

        if (version is null)
        {
            var current = await connection.QuerySingleOrDefaultAsync<long?>(
                new CommandDefinition("select version from wms.picking_tasks where id=@TaskId", new { TaskId = taskId }, tx, cancellationToken: ct));
            await tx.RollbackAsync(ct);
            await Send.ResponseAsync(KbxConflictProblem.Version(current), 409, cancellation: ct);
            return;
        }

        await connection.ExecuteAsync(new CommandDefinition("""
            insert into audit.entries(id, aggregate_type, aggregate_id, action, actor, occurred_at, data)
            values (@Id, 'WmsPickingTask', @TaskId, 'PickingStarted', @Actor, now(), '{}'::jsonb);
            """, new { Id = Guid.NewGuid(), TaskId = taskId, Actor = User.Identity?.Name ?? "unknown" }, tx, cancellationToken: ct));

        await tx.CommitAsync(ct);
        var task = await PickingTaskQueries.GetAsync(connection, null, taskId, ct);
        await SendOkAsync(task!, ct);
    }
}
