/**
 *
 */
package com.braintree.commands.impl;

import com.braintree.command.request.BrainTreeSubmitForSettlementTransactionRequest;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.order.service.BraintreeOrderRetrievalService;
import com.braintree.util.BrainTreeUtils;
import de.hybris.platform.core.model.order.AbstractOrderModel;
import de.hybris.platform.payment.commands.CaptureCommand;
import de.hybris.platform.payment.commands.request.CaptureRequest;
import de.hybris.platform.payment.commands.result.CaptureResult;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;
import de.hybris.platform.payment.enums.PaymentTransactionType;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import java.math.BigDecimal;
import java.util.List;
import java.util.function.Predicate;

import org.apache.log4j.Logger;

/**
 * This abstract class is for capture in subordinate classes.
 */
public abstract class AbstractCaptureCommand extends AbstractCommand implements CaptureCommand {

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

    protected CaptureRequest request;

    protected BraintreeOrderRetrievalService orderRetrievalService;

    @Override
    public CaptureResult perform(final CaptureRequest request) {
        this.request = request;
        LOG.debug("Capture command implementation: " + this.getClass());
        final CaptureResult result;
        final String transactionID = request.getRequestId();

        final AbstractOrderModel order = orderRetrievalService.getOrderForTransaction(transactionID);

        if (isMultiCaptureAvailable(order)) {
            return createReviewNeededResponse();
        } else {
            if (isCaptureResponseShouldBeCreated(order)) {
                result = createCaptureResponse(request.getRequestId());
            } else {
                getLoggingHandler().getLogger()
                    .info(String.format("[CAPTURING FOR TRANSACTION CODE: %s]", transactionID));
                final BrainTreeSubmitForSettlementTransactionRequest captureRequest =
                    new BrainTreeSubmitForSettlementTransactionRequest(transactionID);
                captureRequest.setTransactionId(transactionID);

                if (order.getBrainTreeOrderId() != null) {
                    getLoggingHandler().getLogger().info(String.format("[ORDER CODE: %s]", order.getBrainTreeOrderId()));
                    captureRequest.setOrderId(order.getBrainTreeOrderId());
                }

                final BigDecimal amount = getSubmitForSettlementAmountForOrder(order);
                if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0) {
                    captureRequest.setAmount(amount);
                }

                result = makeApiCall(captureRequest);
            }
        }

        return result;
    }
    
    private boolean isCaptureResponseShouldBeCreated(AbstractOrderModel order) {
        return checkPaypalPayment(BrainTreeUtils::isIntentSale, order) || isSubmitForSettlementAvailable(order)
                || isLPMPayment(order);
    }

    protected abstract CaptureResult makeApiCall(BrainTreeSubmitForSettlementTransactionRequest request);

    protected CaptureResult createReviewNeededResponse() {
        final CaptureResult captureResult = new CaptureResult();
        captureResult.setTransactionStatus(TransactionStatus.REVIEW);
        captureResult.setTransactionStatusDetails(TransactionStatusDetails.REVIEW_NEEDED);
        captureResult.setCurrency(request.getCurrency());
        captureResult.setRequestId(request.getRequestId());
        captureResult.setRequestToken(request.getRequestToken());
        captureResult.setMerchantTransactionCode(request.getMerchantTransactionCode());
        captureResult.setTotalAmount(BigDecimal.ZERO);
        return captureResult;
    }

    protected CaptureResult createCaptureResponse(final String transactionID) {
        getLoggingHandler().getLogger().info(String
            .format(
                "[SETTLEMENT FOR TRANSACTION ID: %s WILL BE SETTLED IMMEDIATELY BY BRAINTREE] See transaction details.",
                transactionID));
        LOG.info("WILL BE SETTLED IMMEDIATELY BY BRAINTREE See transaction transactionID: " + transactionID);
        final CaptureResult result = new CaptureResult();

        result.setCurrency(request.getCurrency());
        result.setMerchantTransactionCode(request.getMerchantTransactionCode());
        result.setRequestId(request.getRequestId());
        result.setRequestToken(request.getRequestToken());
        result.setTransactionStatus(TransactionStatus.ACCEPTED);
        result.setTransactionStatusDetails(TransactionStatusDetails.SUCCESFULL);
        result.setTotalAmount(request.getTotalAmount());

        return result;
    }

    protected BigDecimal getSubmitForSettlementAmountForOrder(AbstractOrderModel orderModel) {
        BigDecimal amount = null;
        final List<PaymentTransactionModel> paymentTransactionModels = orderModel.getPaymentTransactions();
        for (final PaymentTransactionModel transaction : paymentTransactionModels) {
            for (final PaymentTransactionEntryModel transactionEntry : transaction.getEntries()) {
                if (PaymentTransactionType.AUTHORIZATION.equals(transactionEntry.getType())) {
                    amount = transactionEntry.getSubmittedForSettlementAmount();
                    break;
                }
            }
            if (amount != null) {
                break;
            }
        }
        return amount;
    }

    private boolean checkPaypalPayment(Predicate<String> intentType, AbstractOrderModel order) {
        final String paymentProvider = ((BrainTreePaymentInfoModel) order.getPaymentInfo()).getPaymentProvider();
        if (BrainTreeUtils.isPayPalPayment(paymentProvider)) {
            final String intent = ((BrainTreePaymentInfoModel) order.getPaymentInfo()).getPayPalIntent();

            return intentType.test(intent);
        }

        return false;
    }

    protected boolean isMultiCaptureAvailable(AbstractOrderModel order) {
        final String paymentProvider = ((BrainTreePaymentInfoModel) order.getPaymentInfo()).getPaymentProvider();
        final boolean isMultiCaptureEnabled = getBrainTreeConfigService().isMultiCaptureEnabled();
        final boolean isSubmitForSettlement = getBrainTreeConfigService().isSubmitForSettlement();

        return isMultiCaptureEnabled && !isSubmitForSettlement && BrainTreeUtils.isCreditCardPayment(paymentProvider);
    }

    protected boolean isSubmitForSettlementAvailable(AbstractOrderModel order) {
        final String paymentProvider = ((BrainTreePaymentInfoModel) order.getPaymentInfo()).getPaymentProvider();
        final boolean isSubmitForSettlement = getBrainTreeConfigService().isSubmitForSettlement()
                || BrainTreeUtils.isUsBankAccountPayment(paymentProvider);

        return isSubmitForSettlement &&
                !(BrainTreeUtils.isPayPalPayment(paymentProvider) || BrainTreeUtils.isLPMPayment(paymentProvider));
    }

    private boolean isLPMPayment(AbstractOrderModel order) {
        final String paymentProvider = ((BrainTreePaymentInfoModel) order.getPaymentInfo()).getPaymentProvider();

        return BrainTreeUtils.isLPMPayment(paymentProvider);
    }


    public BraintreeOrderRetrievalService getOrderRetrievalService() {
        return orderRetrievalService;
    }

    public void setOrderRetrievalService(final BraintreeOrderRetrievalService orderRetrievalService) {
        this.orderRetrievalService = orderRetrievalService;
    }

}
