using System.Text.Json;
using Dapper;
using FastEndpoints;
using Npgsql;
using Kbx.Shared.Runtime;
using Shared.Problems;

namespace Kbx.Modules.Common.Integrations.Attempts.Retry;

public sealed record Request(string? IdempotencyKey = null);
public sealed record Response(Guid AttemptId,string State,string Message);

public sealed class Endpoint(NpgsqlDataSource dataSource):Endpoint<Request,object>
{
    private const string OperationId = "common.integrations.retryAttempt";
    public override void Configure(){Post("/api/integrations/attempts/{attemptId:guid}/retry");Permissions("common.integration.retry");}

    public override async Task HandleAsync(Request req,CancellationToken ct)
    {
        var attemptId=Route<Guid>("attemptId");
        var key=HttpContext.Request.Headers["Idempotency-Key"].FirstOrDefault()??req.IdempotencyKey;
        if(string.IsNullOrWhiteSpace(key)){
            await Send.ResponseAsync(KbxValidationProblem.Create(new KbxValidationError(null,null,"IDEMPOTENCY_KEY_REQUIRED","안전한 재처리를 위해 Idempotency-Key가 필요합니다.")),400,cancellation:ct);return;
        }
        await using var c=await dataSource.OpenConnectionAsync(ct);
        await using var tx=await c.BeginTransactionAsync(ct);
        var replay=await c.QuerySingleOrDefaultAsync<string?>(new CommandDefinition(
            "select response_json::text from kbx.command_receipts where operation_id=@OperationId and idempotency_key=@Key",
            new { OperationId, Key=key },tx,cancellationToken:ct));
        if(replay is not null){await tx.RollbackAsync(ct);await Send.OkAsync(JsonSerializer.Deserialize<Response>(replay)!,ct);return;}

        const string sql="""
        update kbx.integration_attempts
           set state='retrying', next_retry_at=now(), manual_retry_key=@Key
         where id=@Id and tenant_id=@TenantId and state='failed'
        returning id;
        """;
        var id=await c.ExecuteScalarAsync<Guid?>(new CommandDefinition(sql,new{Id=attemptId,TenantId=RuntimeIdentity.TenantId(User),Key=key},tx,cancellationToken:ct));
        if(id is null){await tx.RollbackAsync(ct);await Send.ResponseAsync(KbxNotFoundProblem.Create("INTEGRATION_ATTEMPT_NOT_RETRYABLE","재처리할 수 있는 연계 실패를 찾지 못했습니다."),404,cancellation:ct);return;}

        var response=new Response(id.Value,"retrying","재처리를 예약했습니다.");
        await c.ExecuteAsync(new CommandDefinition("insert into kbx.command_receipts(operation_id,idempotency_key,response_json) values(@OperationId,@Key,cast(@Json as jsonb))",
            new { OperationId, Key=key, Json=JsonSerializer.Serialize(response) },tx,cancellationToken:ct));
        await tx.CommitAsync(ct);
        await Send.OkAsync(response,ct);
    }
}
