// Path: src/main/java/com/github/sandokandias/payments/application/PaymentProcessManager.java
// public interface PaymentProcessManager {
//     CompletionStage<Either<CommandFailure, Tuple2<PaymentId, PaymentStatus>>> process(PerformPayment command);
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/domain/command/PerformPayment.java
// @Value(staticConstructor = "commandOf")
// public class PerformPayment implements PaymentCommand {
//     private final CustomerId customerId;
//     private final PaymentIntent paymentIntent;
//     private final PaymentMethod paymentMethod;
//     private final Transaction transaction;
//     private final LocalDateTime timestamp = LocalDateTime.now();
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/domain/shared/CommandFailure.java
// @AllArgsConstructor
// public class CommandFailure {
//     public final Set<I18nCode> codes;
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/interfaces/rest/model/PerformPaymentRequest.java
// @Data
// public class PerformPaymentRequest {
//     @NotNull
//     private String customerId;
//     @ValidEnum(conformsTo = PaymentIntent.class)
//     private String paymentIntent;
//     @ValidEnum(conformsTo = PaymentMethod.class)
//     private String paymentMethod;
//     @Valid
//     private Transaction transaction;
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/interfaces/rest/model/PerformPaymentResponse.java
// @AllArgsConstructor
// @Setter
// @Getter
// public class PerformPaymentResponse {
//     private String paymentId;
//     private String status;
// }


import com.github.sandokandias.payments.application.PaymentProcessManager;
import com.github.sandokandias.payments.domain.command.PerformPayment;
import com.github.sandokandias.payments.domain.shared.CommandFailure;
import com.github.sandokandias.payments.domain.vo.*;
import com.github.sandokandias.payments.interfaces.rest.model.PerformPaymentRequest;
import com.github.sandokandias.payments.interfaces.rest.model.PerformPaymentResponse;
import io.vavr.Tuple2;
import io.vavr.control.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionStage;
package com.github.sandokandias.payments.interfaces.rest.controller;



@RestController("/v1/payments")
public class PaymentController {

    private static final Logger LOG = LoggerFactory.getLogger(PaymentController.class);

    private final PaymentProcessManager paymentProcessManager;

    public PaymentController(PaymentProcessManager paymentProcessManager) {
        this.paymentProcessManager = paymentProcessManager;
    }

    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Callable<CompletionStage<ResponseEntity<?>>> process(@Valid @RequestBody PerformPaymentRequest request) {

        LOG.debug("Request {}", request);

        return () -> {
            LOG.debug("Callable...");

            PerformPayment performPayment = PerformPayment.commandOf(
                    new CustomerId(request.getCustomerId()),
                    PaymentIntent.valueOf(request.getPaymentIntent()),
                    PaymentMethod.valueOf(request.getPaymentMethod()),
                    request.getTransaction());

            CompletionStage<Either<CommandFailure, Tuple2<PaymentId, PaymentStatus>>> promise = paymentProcessManager.process(performPayment);
            return promise.thenApply(acceptOrReject -> acceptOrReject.fold(
                    reject -> ResponseEntity.badRequest().body(reject),
// Path: src/main/java/com/github/sandokandias/payments/application/PaymentProcessManager.java
// public interface PaymentProcessManager {
//     CompletionStage<Either<CommandFailure, Tuple2<PaymentId, PaymentStatus>>> process(PerformPayment command);
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/domain/command/PerformPayment.java
// @Value(staticConstructor = "commandOf")
// public class PerformPayment implements PaymentCommand {
//     private final CustomerId customerId;
//     private final PaymentIntent paymentIntent;
//     private final PaymentMethod paymentMethod;
//     private final Transaction transaction;
//     private final LocalDateTime timestamp = LocalDateTime.now();
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/domain/shared/CommandFailure.java
// @AllArgsConstructor
// public class CommandFailure {
//     public final Set<I18nCode> codes;
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/interfaces/rest/model/PerformPaymentRequest.java
// @Data
// public class PerformPaymentRequest {
//     @NotNull
//     private String customerId;
//     @ValidEnum(conformsTo = PaymentIntent.class)
//     private String paymentIntent;
//     @ValidEnum(conformsTo = PaymentMethod.class)
//     private String paymentMethod;
//     @Valid
//     private Transaction transaction;
// }
// 
// Path: src/main/java/com/github/sandokandias/payments/interfaces/rest/model/PerformPaymentResponse.java
// @AllArgsConstructor
// @Setter
// @Getter
// public class PerformPaymentResponse {
//     private String paymentId;
//     private String status;
// }


// Path: src/main/java/com/github/sandokandias/payments/interfaces/rest/controller/PaymentController.java
import com.github.sandokandias.payments.application.PaymentProcessManager;
import com.github.sandokandias.payments.domain.command.PerformPayment;
import com.github.sandokandias.payments.domain.shared.CommandFailure;
import com.github.sandokandias.payments.domain.vo.*;
import com.github.sandokandias.payments.interfaces.rest.model.PerformPaymentRequest;
import com.github.sandokandias.payments.interfaces.rest.model.PerformPaymentResponse;
import io.vavr.Tuple2;
import io.vavr.control.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionStage;

package com.github.sandokandias.payments.interfaces.rest.controller;



@RestController("/v1/payments")
public class PaymentController {

    private static final Logger LOG = LoggerFactory.getLogger(PaymentController.class);

    private final PaymentProcessManager paymentProcessManager;

    public PaymentController(PaymentProcessManager paymentProcessManager) {
        this.paymentProcessManager = paymentProcessManager;
    }

    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Callable<CompletionStage<ResponseEntity<?>>> process(@Valid @RequestBody PerformPaymentRequest request) {

        LOG.debug("Request {}", request);

        return () -> {
            LOG.debug("Callable...");

            PerformPayment performPayment = PerformPayment.commandOf(
                    new CustomerId(request.getCustomerId()),
                    PaymentIntent.valueOf(request.getPaymentIntent()),
                    PaymentMethod.valueOf(request.getPaymentMethod()),
                    request.getTransaction());

            CompletionStage<Either<CommandFailure, Tuple2<PaymentId, PaymentStatus>>> promise = paymentProcessManager.process(performPayment);
            return promise.thenApply(acceptOrReject -> acceptOrReject.fold(
                    reject -> ResponseEntity.badRequest().body(reject),
                    accept -> ResponseEntity.accepted().body(new PerformPaymentResponse(accept._1.id, accept._2.name()))