using Dapper;
using FastEndpoints;
using Npgsql;
using Shared.Problems;

namespace Modules.OMS.Claims.Workflow;

public sealed record ClaimTransitionRequest(Guid Id);
public sealed record ClaimTransitionResponse(Guid Id, string Status, long Version);

public sealed class ClaimTransitionHandler(NpgsqlDataSource dataSource)
{
    public async Task<ClaimTransitionResponse?> HandleAsync(
        Guid id,
        string fromStatus,
        string toStatus,
        CancellationToken ct)
    {
        await using var connection = await dataSource.OpenConnectionAsync(ct);
        return await connection.QuerySingleOrDefaultAsync<ClaimTransitionResponse>(new CommandDefinition("""
            update oms.claims
               set status=@ToStatus, version=version+1
             where id=@Id and status=@FromStatus
            returning id, status, version;
            """, new { Id = id, FromStatus = fromStatus, ToStatus = toStatus }, cancellationToken: ct));
    }
}

public sealed class ApproveEndpoint(ClaimTransitionHandler handler)
    : Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
{
    public override void Configure() { Post("/api/oms/claims/{id:guid}/approve"); Permissions("oms.claim.approve"); }
    public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
        => await RespondAsync(await handler.HandleAsync(req.Id, "REQUESTED", "APPROVED", ct), "approve", "REQUESTED", ct);

    private async Task RespondAsync(ClaimTransitionResponse? result, string transition, string required, CancellationToken ct)
    {
        if (result is not null) { await Send.OkAsync(result, ct); return; }
        await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "현재 상태에서는 승인할 수 없습니다.", $"Transition '{transition}' requires state '{required}'."), 409, cancellation: ct);
    }
}

public sealed class HoldEndpoint(ClaimTransitionHandler handler)
    : Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
{
    public override void Configure() { Post("/api/oms/claims/{id:guid}/hold"); Permissions("oms.claim.hold"); }
    public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
    {
        var result = await handler.HandleAsync(req.Id, "REQUESTED", "HOLD", ct);
        if (result is not null) { await Send.OkAsync(result, ct); return; }
        await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "현재 상태에서는 보류할 수 없습니다."), 409, cancellation: ct);
    }
}

public sealed class StartEndpoint(ClaimTransitionHandler handler)
    : Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
{
    public override void Configure() { Post("/api/oms/claims/{id:guid}/start"); Permissions("oms.claim.process"); }
    public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
    {
        var result = await handler.HandleAsync(req.Id, "APPROVED", "IN_PROGRESS", ct);
        if (result is not null) { await Send.OkAsync(result, ct); return; }
        await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "승인된 클레임만 처리를 시작할 수 있습니다."), 409, cancellation: ct);
    }
}

public sealed class CompleteEndpoint(ClaimTransitionHandler handler)
    : Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
{
    public override void Configure() { Post("/api/oms/claims/{id:guid}/complete"); Permissions("oms.claim.process"); }
    public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
    {
        var result = await handler.HandleAsync(req.Id, "IN_PROGRESS", "COMPLETED", ct);
        if (result is not null) { await Send.OkAsync(result, ct); return; }
        await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "처리 중인 클레임만 완료할 수 있습니다."), 409, cancellation: ct);
    }
}
