package com.braintree.order.capture.partial.strategy;

import com.braintree.enums.BrainTreePaymentMethod;
import com.braintree.enums.BraintreePaymentTransactionStatus;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.order.capture.partial.strategy.impl.DefaultBraintreePartialOrderCalculationStrategy;
import com.braintree.transaction.service.BrainTreeTransactionService;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.AbstractOrderModel;
import de.hybris.platform.core.model.order.OrderModel;
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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

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

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;

@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class DefaultBraintreePartialOrderCalculationStrategyTest {

    private static final Double TOTAL_PRICE = 200d;
    private static final BigDecimal EXCEPTED = BigDecimal.valueOf(190d);

    @Mock
    private BrainTreeTransactionService brainTreeTransactionService;

    @InjectMocks
    private DefaultBraintreePartialOrderCalculationStrategy unit;

    @Mock
    private OrderModel orderModel;
    @Mock
    private PaymentTransactionModel paymentTransactionModel;
    @Mock
    private PaymentTransactionEntryModel entryModel;
    @Mock
    private AbstractOrderModel abstractOrderModel;
    @Mock
    private BrainTreePaymentInfoModel paymentInfoModel;

    private List<PaymentTransactionModel> paymentTransactionModels;
    private List<PaymentTransactionEntryModel> entryModels;

    @Before
    public void setUp() {
        paymentTransactionModels = new ArrayList<>();
        entryModels = new ArrayList<>();
        when(paymentTransactionModel.getOrder()).thenReturn(abstractOrderModel);
        when(abstractOrderModel.getPaymentInfo()).thenReturn(paymentInfoModel);
    }

    @Test
    public void calculateCaptureAmountForOrderWithoutTransactionsShouldReturnZero() {
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);
        assertEquals(BigDecimal.ZERO, unit.calculateCaptureAmount(orderModel));
    }

    @Test
    public void testCalculateCaptureAmountWithOneTransaction() {
        paymentTransactionModels.add(paymentTransactionModel);
        entryModels.add(entryModel);

        when(entryModel.getType()).thenReturn(PaymentTransactionType.CAPTURE);
        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL.name());
        when(entryModel.getAmount()).thenReturn(BigDecimal.TEN);
        when(paymentTransactionModel.getEntries()).thenReturn(entryModels);
        when(orderModel.getTotalPrice()).thenReturn(TOTAL_PRICE);
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);
        when(brainTreeTransactionService.calculateTotalAmount(any())).thenReturn(BigDecimal.valueOf(TOTAL_PRICE));

        assertEquals(EXCEPTED, unit.calculateCaptureAmount(orderModel));
    }

    @Test
    public void shouldReturnZeroIfTransactionIsNotAvailableForSettlement() {
        paymentTransactionModels.add(paymentTransactionModel);
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.SETTLEMENT_DECLINED);

        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);
        assertEquals(BigDecimal.ZERO, unit.calculateCaptureAmount(orderModel));
    }

    @Test
    public void shouldReturnZeroIfPayPalTransactionIsNotAvailableForSettlement() {
        //given
        paymentTransactionModels.add(paymentTransactionModel);
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.SETTLED);
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);

        //when
        BigDecimal result = unit.calculateCaptureAmount(orderModel);

        //then
        assertEquals(BigDecimal.ZERO, result);
    }

    @Test
    public void shouldCalculateCaptureAmountIfPayPalTransactionHasAuthorizedStatus() {
        //given
        paymentTransactionModels.add(paymentTransactionModel);
        entryModels.add(entryModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.AUTHORIZED);
        when(entryModel.getType()).thenReturn(PaymentTransactionType.PARTIAL_CAPTURE);
        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL.name());
        when(entryModel.getAmount()).thenReturn(BigDecimal.TEN);
        when(paymentTransactionModel.getEntries()).thenReturn(entryModels);
        when(orderModel.getTotalPrice()).thenReturn(TOTAL_PRICE);
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);
        when(brainTreeTransactionService.calculateTotalAmount(any())).thenReturn(BigDecimal.valueOf(TOTAL_PRICE));

        //when
        BigDecimal result = unit.calculateCaptureAmount(orderModel);

        //then
        assertEquals(EXCEPTED, result);
    }

    @Test
    public void shouldCalculateCaptureAmountIfPayPalTransactionHasSettlementPendingStatus() {
        //given
        paymentTransactionModels.add(paymentTransactionModel);
        entryModels.add(entryModel);

        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentTransactionModel.getBraintreePaymentTransactionStatus()).thenReturn(BraintreePaymentTransactionStatus.SETTLEMENT_PENDING);
        when(entryModel.getType()).thenReturn(PaymentTransactionType.PARTIAL_CAPTURE);
        when(entryModel.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED.name());
        when(entryModel.getTransactionStatusDetails()).thenReturn(TransactionStatusDetails.SUCCESFULL.name());
        when(entryModel.getAmount()).thenReturn(BigDecimal.TEN);
        when(paymentTransactionModel.getEntries()).thenReturn(entryModels);
        when(orderModel.getTotalPrice()).thenReturn(TOTAL_PRICE);
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactionModels);
        when(brainTreeTransactionService.calculateTotalAmount(any())).thenReturn(BigDecimal.valueOf(TOTAL_PRICE));

        //when
        BigDecimal result = unit.calculateCaptureAmount(orderModel);

        //then
        assertEquals(EXCEPTED, result);
    }

}
