package com.braintree.order.submitForSettlement.service.impl;

import com.braintree.command.request.BrainTreeSubmitForSettlementTransactionRequest;
import com.braintree.command.result.BrainTreeAbstractTransactionResult;
import com.braintree.command.result.BrainTreeSubmitForSettlementTransactionResult;
import com.braintree.exceptions.BraintreeErrorException;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.order.adjust.authorization.BraintreeAdjustAuthorizationService;
import com.braintree.order.submitForSettlement.service.BraintreeSubmitForSettlementService;
import com.braintree.transaction.service.BrainTreePaymentTransactionService;
import com.braintree.transaction.service.BrainTreeTransactionService;
import de.hybris.platform.core.enums.OrderStatus;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.enums.PaymentTransactionType;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.servicelayer.model.ModelService;
import org.apache.commons.collections.CollectionUtils;

import java.math.BigDecimal;
import java.util.List;

import static de.hybris.platform.servicelayer.util.ServicesUtil.validateParameterNotNull;


public class DefaultBraintreeSubmitForSettlementService implements BraintreeSubmitForSettlementService {

    private static final String ORDER_MODEL_CAN_NOT_BE_NULL_MESSAGE = "orderModel can not be null";

    private BrainTreePaymentService brainTreePaymentService;
    private BrainTreePaymentTransactionService brainTreePaymentTransactionService;
    private BrainTreeTransactionService brainTreeTransactionService;
    private ModelService modelService;
    private BraintreeAdjustAuthorizationService braintreeAdjustAuthorizationService;

    @Override
    public PaymentTransactionEntryModel submitForSettlement(OrderModel orderModel, BigDecimal amount,
                                                            PaymentTransactionEntryModel entry, boolean isOrderToBeForceCompleted)
            throws BraintreeErrorException {
        validateParameterNotNull(orderModel, ORDER_MODEL_CAN_NOT_BE_NULL_MESSAGE);

        BrainTreeSubmitForSettlementTransactionRequest request =
                new BrainTreeSubmitForSettlementTransactionRequest(orderModel.getUser().getUid());
        request.setAmount(amount);
        request.setTransactionId(entry.getRequestId());
        request.setOrderId(orderModel.getBrainTreeOrderId());

        BrainTreeSubmitForSettlementTransactionResult brainTreeSubmitForSettlementTransactionResult = brainTreePaymentService
                .submitForSettlementTransaction(request);

        PaymentTransactionEntryModel transactionEntry = brainTreeTransactionService.createPaymentTransaction(entry.getPaymentTransaction(),
                brainTreeSubmitForSettlementTransactionResult, amount, PaymentTransactionType.CAPTURE, entry.getRequestId());

        if (brainTreeSubmitForSettlementTransactionResult.isSuccess()) {
            if (brainTreePaymentTransactionService.isOrderFullyCaptured(entry) || isOrderToBeForceCompleted) {
                brainTreePaymentTransactionService.setOrderStatus(orderModel, OrderStatus.PAYMENT_CAPTURED);
                brainTreePaymentTransactionService.continueOrderProcess(orderModel);
            } else {
                brainTreePaymentTransactionService.setOrderStatus(orderModel, OrderStatus.PARTIAL_CAPTURE);
            }
        } else {
            processErrorResult(brainTreeSubmitForSettlementTransactionResult, transactionEntry);
        }
        brainTreeTransactionService.saveBraintreeTransactionStatus(
                brainTreeSubmitForSettlementTransactionResult.getStatus(), transactionEntry.getPaymentTransaction());
        return transactionEntry;
    }

    @Override
    public boolean isSubmitForSettlementAvailable(OrderModel orderModel) {
        modelService.refresh(orderModel);
        final List<PaymentTransactionModel> paymentTransactions = orderModel.getPaymentTransactions();
        if (CollectionUtils.isNotEmpty(paymentTransactions)) {
            return paymentTransactions
                    .stream()
                    .flatMap(transaction -> transaction.getEntries().stream())
                    .noneMatch(entry -> PaymentTransactionType.CAPTURE.equals(entry.getType())
                            && TransactionStatus.ACCEPTED.name().equals(entry.getTransactionStatus()));
        }
        return Boolean.FALSE;
    }

    private void processErrorResult(BrainTreeAbstractTransactionResult transactionResult,
                                    PaymentTransactionEntryModel transactionEntry) {
        braintreeAdjustAuthorizationService.checkAndProcessHardDeclineError(transactionResult, transactionEntry);
    }

    public void setBrainTreePaymentService(BrainTreePaymentService brainTreePaymentService) {
        this.brainTreePaymentService = brainTreePaymentService;
    }

    public void setBrainTreeTransactionService(BrainTreeTransactionService brainTreeTransactionService) {
        this.brainTreeTransactionService = brainTreeTransactionService;
    }

    public void setBrainTreePaymentTransactionService(
            BrainTreePaymentTransactionService brainTreePaymentTransactionService) {
        this.brainTreePaymentTransactionService = brainTreePaymentTransactionService;
    }

    public void setModelService(ModelService modelService) {
        this.modelService = modelService;
    }

    public void setBraintreeAdjustAuthorizationService(BraintreeAdjustAuthorizationService braintreeAdjustAuthorizationService) {
        this.braintreeAdjustAuthorizationService = braintreeAdjustAuthorizationService;
    }

}
