package com.braintree.customersupportbackoffice.facade.impl;

import com.braintree.command.request.BrainTreeCloneTransactionRequest;
import com.braintree.command.request.BrainTreeCreateCreditCardPaymentMethodRequest;
import com.braintree.command.request.BrainTreeCustomerRequest;
import com.braintree.command.request.BrainTreeFindTransactionRequest;
import com.braintree.command.request.BrainTreeRefundTransactionRequest;
import com.braintree.command.request.BrainTreeSaleTransactionRequest;
import com.braintree.command.result.BrainTreeAbstractTransactionResult;
import com.braintree.command.result.BrainTreeCloneTransactionResult;
import com.braintree.command.result.BrainTreeCustomerResult;
import com.braintree.command.result.BrainTreeFindCustomersResult;
import com.braintree.command.result.BrainTreeFindTransactionResult;
import com.braintree.command.result.BrainTreePaymentMethodResult;
import com.braintree.command.result.BrainTreeRefundTransactionResult;
import com.braintree.command.result.BrainTreeSaleTransactionResult;
import com.braintree.command.result.BrainTreeVoidResult;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customersupportbackoffice.data.BrainTreeTransactionInfo;
import com.braintree.customersupportbackoffice.services.BrainTreeCustomerSearchService;
import com.braintree.customersupportbackoffice.services.BrainTreeTransactionSearchService;
import com.braintree.hybris.data.BrainTreeResponseResultData;
import com.braintree.hybris.data.BraintreeTransactionData;
import com.braintree.hybris.data.BraintreeTransactionEntryData;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.model.BrainTreeTransactionDetailModel;
import com.braintree.model.BraintreeCustomerDetailsModel;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import com.braintree.transaction.service.BrainTreeTransactionService;
import com.braintreegateway.PaymentMethod;
import com.hybris.backoffice.widgets.advancedsearch.impl.AdvancedSearchData;
import com.hybris.backoffice.widgets.advancedsearch.impl.SearchConditionData;
import com.hybris.cockpitng.search.data.ValueComparisonOperator;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.order.OrderModel;
import de.hybris.platform.core.model.user.UserModel;
import de.hybris.platform.payment.commands.request.CreateSubscriptionRequest;
import de.hybris.platform.payment.commands.request.VoidRequest;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.model.PaymentTransactionEntryModel;
import de.hybris.platform.payment.model.PaymentTransactionModel;
import de.hybris.platform.servicelayer.dto.converter.Converter;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.session.SessionService;
import de.hybris.platform.servicelayer.user.UserService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import org.zkoss.zul.Messagebox;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String TOKEN = "TOKEN";
    private static final String FIRST_NAME = "FIRST_NAME";
    private static final String LAST_NAME = "LAST_NAME";
    private static final String EMAIL = "EMAIL";
    private static final String POST_CODE = "POST_CODE";
    private static final String STREET = "STREET";
    private static final String ID = "ID";
    private static final String BRAINTREE_ID = "BRAINTREE_ID";
    private static final String RISK_DESITION = "RISK_DESITION";
    private static final String PAYMENT_METHOD_NONCE = "PAYMENT_METHOD_NONCE";
    private static final String CARD_HOLDER_NAME = "CARD_HOLDER_NAME";
    private static final String CARD_NUMBER = "CARD_NUMBER";
    private static final String CVV = "CVV";
    private static final String EXPIRATION_DATE = "01/30";
    private static final String BILLING_ADDRESS_ID = "BILLING_ADDRESS_ID";
    private static final String AMOUNT = "19.99";
    private static final String BRAINTREE_TRANSACTION_CUSTOMER_ID = "braintreeTransactionCustomerID";
    private static final String BRAINTREE_TRANSACTION_CUSTOMER_EMAIL = "braintreeTransactionCustomerEmail";
    private static final String BRAINTREE_TRANSACTION_DETAIL_ID = "braintreeTransactionDetailID";
    private static final String BRAINTREE_TRANSACTION_DETAIL_STATUS = "braintreeTransactionDetailStatus";
    private static final String BRAINTREE_TRANSACTION_DETAIL_RISK_DECISION = "braintreeTransactionDetailRiskDecision";
    private static final String BRAINTREE_TRANSACTION_DETAIL_DATE_FROM = "braintreeTransactionDetailDateFrom";
    private static final String BRAINTREE_TRANSACTION_DETAIL_DATE_TO = "braintreeTransactionDetailDateTo";

    @Mock
    private UserModel user;

    @Mock
    private ModelService modelService;
    @Mock
    private UserService userService;
    @Mock
    private BrainTreeCustomerSearchService customerSearchService;
    @Mock
    private BrainTreeTransactionSearchService transactionSearchService;
    @Mock
    private BrainTreePaymentService brainTreePaymentService;
    @Mock
    private SessionService sessionService;
    @Mock
    private BrainTreeTransactionService brainTreeTransactionService;
    @Mock
    private BraintreePaymentInfoService paymentInfoService;
    @Mock
    private BrainTreeConfigService brainTreeConfigService;
    @Mock
    private Converter<BraintreeTransactionEntryData, BrainTreeTransactionDetailModel> transactionDetailPopulator;
    @Mock
    private Converter<BrainTreeAbstractTransactionResult, BrainTreeResponseResultData> brainTreeCustomerSupportResponseConverter;
    @Mock
    private Converter<PaymentMethod, BrainTreePaymentInfoModel> paymentMethodConverter;
    @Mock
    private Converter<BrainTreePaymentMethodResult, BrainTreePaymentInfoModel> paymentMethodGraphQLConverter;

    @Captor
    private ArgumentCaptor<CreateSubscriptionRequest> createSubscriptionRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeCreateCreditCardPaymentMethodRequest> createCreditCardPaymentMethodRequestCaptor;
    @Captor
    private ArgumentCaptor<BrainTreeSaleTransactionRequest> saleTransactionRequestCaptor;

    @Spy
    @InjectMocks
    private DefaultBrainTreeCustomerSupportFacade unit;

    @Before
    public void setUp() {
        when(userService.getCurrentUser()).thenReturn(user);
        when(user.getUid()).thenReturn(BRAINTREE_ID);
    }


    @Test
    public void shouldFindCustomer() {
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);
        BraintreeCustomerDetailsModel customerDetailsModel = mock(BraintreeCustomerDetailsModel.class);
        List<BraintreeCustomerDetailsModel> customerDetailsModels = Arrays.asList(customerDetailsModel);

        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(customers.getCustomers()).thenReturn(customerDetailsModels);

        BraintreeCustomerDetailsModel result = unit.findCustomer(ID);

        assertEquals(customerDetailsModel, result);
        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(customerSearchService).findCustomers(any(BrainTreeCustomerRequest.class));
        verify(customers, times(3)).getCustomers();
    }

    @Test
    public void shouldReturnNullWhenCustomersIsNullFindCustomer() {
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);
        BraintreeCustomerDetailsModel customerDetailsModel = mock(BraintreeCustomerDetailsModel.class);
        List<BraintreeCustomerDetailsModel> customerDetailsModels = Arrays.asList(customerDetailsModel);

        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(customers.getCustomers()).thenReturn(null);

        BraintreeCustomerDetailsModel result = unit.findCustomer(ID);

        assertNull(result);
    }

    @Test
    public void shouldReturnNullWhenCustomersIsEmpty() {
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);
        BraintreeCustomerDetailsModel customerDetailsModel = mock(BraintreeCustomerDetailsModel.class);
        List<BraintreeCustomerDetailsModel> customerDetailsModels = new ArrayList<>();

        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(customers.getCustomers()).thenReturn(customerDetailsModels);

        BraintreeCustomerDetailsModel result = unit.findCustomer(ID);

        assertNull(result);
    }

    @Test
    public void shouldNotFindCustomer() {
        when(user.getUid()).thenReturn(null);

        BraintreeCustomerDetailsModel result = unit.findCustomer(ID);

        assertEquals(null, result);
        verify(userService).getCurrentUser();
        verify(user).getUid();//    @Test

    }

    @Test
    public void shoulFindCustomers() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);
        SearchConditionData customerIdData = mock(SearchConditionData.class);
        List<SearchConditionData> customerIds = Arrays.asList(customerIdData);
        SearchConditionData customerEmailData = mock(SearchConditionData.class);
        List<SearchConditionData> customerEmails = Arrays.asList(customerEmailData);
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);
        List<BraintreeCustomerDetailsModel> customerDetailsModels = new ArrayList<>();

        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID)).thenReturn(customerIds);
        when(customerIdData.getValue()).thenReturn(ID);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL)).thenReturn(customerEmails);
        when(customerEmailData.getValue()).thenReturn(EMAIL);
        when(customerEmailData.getOperator()).thenReturn(ValueComparisonOperator.CONTAINS);
        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(brainTreeConfigService.getBackofficeSearchLimit()).thenReturn(1);
        when(customers.isExceedLimit()).thenReturn(false);
        when(customers.getCustomers()).thenReturn(customerDetailsModels);

        List<BraintreeCustomerDetailsModel> result = unit.findCustomers(searchData);

        assertEquals(customerDetailsModels, result);
        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(searchData).getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID);
        verify(customerIdData, times(2)).getValue();
        verify(searchData).getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL);
        verify(customerEmailData, times(2)).getValue();
        verify(customerEmailData).getOperator();
        verify(customerSearchService).findCustomers(any(BrainTreeCustomerRequest.class));
        verify(brainTreeConfigService).getBackofficeSearchLimit();
        verify(customers).isExceedLimit();
        verify(customers).getCustomers();
    }

    @Test
    public void shouldShowMessageAndReturnEmptyListCustomersWhenCustomerIdIsNull() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);

        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID)).thenReturn(null);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL)).thenReturn(null);
        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(brainTreeConfigService.getBackofficeSearchLimit()).thenReturn(1);
        when(customers.isExceedLimit()).thenReturn(true);
        try(MockedStatic<Messagebox> messageboxMockedStatic = mockStatic(Messagebox.class)) {
            List<BraintreeCustomerDetailsModel> result = unit.findCustomers(searchData);
            assertEquals(Collections.emptyList(), result);
        }
    }

    @Test
    public void shouldShowMessageAndReturnEmptyListCustomersWhenCustomerIdIsEmpty() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);
        BrainTreeFindCustomersResult customers = mock(BrainTreeFindCustomersResult.class);

        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID)).thenReturn(Collections.emptyList());
        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL)).thenReturn(Collections.emptyList());
        when(customerSearchService.findCustomers(any(BrainTreeCustomerRequest.class))).thenReturn(customers);
        when(brainTreeConfigService.getBackofficeSearchLimit()).thenReturn(1);
        when(customers.isExceedLimit()).thenReturn(true);
        try(MockedStatic<Messagebox> messageboxMockedStatic = mockStatic(Messagebox.class)) {
            List<BraintreeCustomerDetailsModel> result = unit.findCustomers(searchData);
            assertEquals(Collections.emptyList(), result);
        }
    }

    @Test
    public void shoulNotFindCustomers() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);

        when(user.getUid()).thenReturn(null);

        List<BraintreeCustomerDetailsModel> result = unit.findCustomers(searchData);

        assertTrue(result.isEmpty());
        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldFindTransactions() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);

        SearchConditionData customerIdData = mock(SearchConditionData.class);
        List<SearchConditionData> customerIdList = Arrays.asList(customerIdData);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID)).thenReturn(customerIdList);
        when(customerIdData.getValue()).thenReturn(ID);

        SearchConditionData customerEmailData = mock(SearchConditionData.class);
        List<SearchConditionData> customerEmailList = Arrays.asList(customerEmailData);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL)).thenReturn(customerEmailList);
        when(customerEmailData.getValue()).thenReturn(EMAIL);
        when(customerEmailData.getOperator()).thenReturn(ValueComparisonOperator.CONTAINS);

        SearchConditionData detailIDData = mock(SearchConditionData.class);
        List<SearchConditionData> detailIDList = Arrays.asList(detailIDData);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_DETAIL_ID)).thenReturn(detailIDList);
        when(detailIDData.getValue()).thenReturn(ID);

        SearchConditionData detailStatusData = mock(SearchConditionData.class);
        List<SearchConditionData> detailStatusList = Arrays.asList(detailStatusData);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_DETAIL_STATUS)).thenReturn(detailStatusList);
        when(detailStatusData.getValue()).thenReturn(TransactionStatus.ACCEPTED);

        SearchConditionData detailRiskDecisionData = mock(SearchConditionData.class);
        List<SearchConditionData> detailRiskDecisionList = Arrays.asList(detailRiskDecisionData);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_DETAIL_RISK_DECISION)).thenReturn(detailRiskDecisionList);
        when(detailRiskDecisionData.getValue()).thenReturn(RISK_DESITION);

        SearchConditionData detailDateFromData = mock(SearchConditionData.class);
        List<SearchConditionData> detailDateFromList = Arrays.asList(detailDateFromData);
        Date dateFrom = mock(Date.class);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_DETAIL_DATE_FROM)).thenReturn(detailDateFromList);
        when(detailDateFromData.getValue()).thenReturn(dateFrom);

        SearchConditionData detailDateToData = mock(SearchConditionData.class);
        List<SearchConditionData> detailDateToList = Arrays.asList(detailDateToData);
        Date dateTo = mock(Date.class);
        when(searchData.getConditions(BRAINTREE_TRANSACTION_DETAIL_DATE_TO)).thenReturn(detailDateToList);
        when(detailDateToData.getValue()).thenReturn(dateTo);

        final BrainTreeFindTransactionResult transactions = mock(BrainTreeFindTransactionResult.class);
        when(transactionSearchService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(transactions);

        when(brainTreeConfigService.getBackofficeSearchLimit()).thenReturn(1);
        when(transactions.isExceedLimit()).thenReturn(false);

        BraintreeTransactionData transactionData = mock(BraintreeTransactionData.class);
        when(transactions.getTransactionData()).thenReturn(transactionData);

        BraintreeTransactionEntryData entryData = mock(BraintreeTransactionEntryData.class);
        List<BraintreeTransactionEntryData> entryDataList = Arrays.asList(entryData);
        when(transactionData.getTransactionEntries()).thenReturn(entryDataList);

        BrainTreeTransactionDetailModel detailModel = mock(BrainTreeTransactionDetailModel.class);
        when(transactionDetailPopulator.convert(entryData)).thenReturn(detailModel);
        when(detailModel.getRiskDecision()).thenReturn(RISK_DESITION);

        List<BrainTreeTransactionDetailModel> result = unit.findTransactions(searchData);

        assertFalse(result.isEmpty());

        verify(userService).getCurrentUser();
        verify(user).getUid();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_CUSTOMER_ID);
        verify(customerIdData, times(2)).getValue();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_CUSTOMER_EMAIL);
        verify(customerEmailData, times(2)).getValue();
        verify(customerEmailData).getOperator();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_DETAIL_ID);
        verify(detailIDData, times(2)).getValue();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_DETAIL_STATUS);
        verify(detailStatusData, times(2)).getValue();

        verify(searchData, times(2)).getConditions(BRAINTREE_TRANSACTION_DETAIL_RISK_DECISION);
        verify(detailRiskDecisionData, times(4)).getValue();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_DETAIL_DATE_FROM);
        verify(detailDateFromData, times(2)).getValue();

        verify(searchData).getConditions(BRAINTREE_TRANSACTION_DETAIL_DATE_TO);
        verify(detailDateToData, times(2)).getValue();

        verify(transactionSearchService).findTransactions(any(BrainTreeFindTransactionRequest.class));

        verify(brainTreeConfigService).getBackofficeSearchLimit();
        verify(transactions).isExceedLimit();

        verify(transactions, times(2)).getTransactionData();

        verify(transactionData, times(2)).getTransactionEntries();

        verify(transactionDetailPopulator).convert(entryData);
        verify(detailModel).getRiskDecision();
    }

    @Test
    public void shouldNotFindTransactions() {
        AdvancedSearchData searchData = mock(AdvancedSearchData.class);

        when(user.getUid()).thenReturn(null);

        List<BrainTreeTransactionDetailModel> result = unit.findTransactions(searchData);

        assertTrue(result.isEmpty());

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldFindTransaction() {
        BrainTreeFindTransactionResult transactions = mock(BrainTreeFindTransactionResult.class);
        BraintreeTransactionData convert = mock(BraintreeTransactionData.class);
        BraintreeTransactionEntryData entryData = mock(BraintreeTransactionEntryData.class);
        List<BraintreeTransactionEntryData> brainTreeTransactions = Arrays.asList(entryData);

        when(transactionSearchService.findTransactions(any(BrainTreeFindTransactionRequest.class))).thenReturn(transactions);
        when(transactions.getTransactionData()).thenReturn(convert);
        when(convert.getTransactionEntries()).thenReturn(brainTreeTransactions);

        BraintreeTransactionEntryData result = unit.findTransaction(ID);
        assertEquals(entryData, result);

        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(transactionSearchService).findTransactions(any(BrainTreeFindTransactionRequest.class));
        verify(transactions).getTransactionData();
        verify(convert, times(2)).getTransactionEntries();
    }

    @Test
    public void shouldNotFindTransaction() {
        when(user.getUid()).thenReturn(null);

        BraintreeTransactionEntryData result = unit.findTransaction(ID);
        assertEquals(null, result);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldRefundTransaction() {
        BrainTreeTransactionDetailModel currentTransaction = mock(BrainTreeTransactionDetailModel.class);
        BrainTreeRefundTransactionResult result = mock(BrainTreeRefundTransactionResult.class);

        when(currentTransaction.getId()).thenReturn(ID);
        when(brainTreePaymentService.refundTransaction(any(BrainTreeRefundTransactionRequest.class))).thenReturn(result);
        when(result.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);

        OrderModel linkedOrder = mock(OrderModel.class);
        PaymentTransactionModel transaction = mock(PaymentTransactionModel.class);
        List<PaymentTransactionModel> paymentTransactions = Arrays.asList(transaction);
        PaymentTransactionEntryModel entryModel = mock(PaymentTransactionEntryModel.class);
        BrainTreeResponseResultData responseResultData = mock(BrainTreeResponseResultData.class);

        when(currentTransaction.getLinkedOrder()).thenReturn(linkedOrder);
        when(linkedOrder.getPaymentTransactions()).thenReturn(paymentTransactions);
        when(brainTreeTransactionService.createRefundTransaction(transaction, result)).thenReturn(entryModel);

        when(brainTreeCustomerSupportResponseConverter.convert(result)).thenReturn(responseResultData);

        BrainTreeResponseResultData resultTest = unit.refundTransaction(currentTransaction, AMOUNT);

        assertEquals(responseResultData, resultTest);
        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(currentTransaction).getId();
        verify(brainTreePaymentService).refundTransaction(any(BrainTreeRefundTransactionRequest.class));
        verify(result).getTransactionStatus();

        verify(currentTransaction).getLinkedOrder();
        verify(linkedOrder).getPaymentTransactions();
        verify(brainTreeTransactionService).createRefundTransaction(transaction, result);
    }

    @Test
    public void shouldNotRefundTransaction() {
        BrainTreeTransactionDetailModel currentTransaction = mock(BrainTreeTransactionDetailModel.class);

        when(user.getUid()).thenReturn(null);

        unit.refundTransaction(currentTransaction, AMOUNT);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldVoidTransaction() {
        BrainTreeTransactionDetailModel transaction = mock(BrainTreeTransactionDetailModel.class);
        BrainTreeVoidResult voidResult = mock(BrainTreeVoidResult.class);
        OrderModel orderModel = mock(OrderModel.class);
        PaymentTransactionModel paymentTransaction = mock(PaymentTransactionModel.class);
        List<PaymentTransactionModel> paymentTransactions = Arrays.asList(paymentTransaction);
        PaymentTransactionEntryModel entryModel = mock(PaymentTransactionEntryModel.class);
        BrainTreeResponseResultData responseResultData = mock(BrainTreeResponseResultData.class);

        when(brainTreePaymentService.voidTransaction(any(VoidRequest.class))).thenReturn(voidResult);
        when(voidResult.getTransactionStatus()).thenReturn(TransactionStatus.ACCEPTED);
        when(transaction.getLinkedOrder()).thenReturn(orderModel);
        when(orderModel.getPaymentTransactions()).thenReturn(paymentTransactions);
        when(brainTreeTransactionService.createCancelTransaction(paymentTransaction, voidResult)).thenReturn(entryModel);
        doNothing().when(modelService).save(orderModel);
        when(brainTreeCustomerSupportResponseConverter.convert(voidResult)).thenReturn(responseResultData);

        BrainTreeResponseResultData result = unit.voidTransaction(transaction);

        assertEquals(responseResultData, result);

        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(brainTreePaymentService).voidTransaction(any(VoidRequest.class));
        verify(voidResult).getTransactionStatus();
        verify(transaction).getLinkedOrder();
        verify(orderModel).getPaymentTransactions();
        verify(brainTreeTransactionService).createCancelTransaction(paymentTransaction, voidResult);
        verify(modelService).save(orderModel);
        verify(brainTreeCustomerSupportResponseConverter).convert(voidResult);
    }

    @Test
    public void shouldNotVoidTransaction() {
        BrainTreeTransactionDetailModel transaction = mock(BrainTreeTransactionDetailModel.class);

        when(user.getUid()).thenReturn(null);

        unit.voidTransaction(transaction);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldCloneTransaction() {
        BrainTreeTransactionDetailModel currentTransaction = mock(BrainTreeTransactionDetailModel.class);
        BrainTreeCloneTransactionResult result = mock(BrainTreeCloneTransactionResult.class);
        BrainTreeResponseResultData responseResultData = mock(BrainTreeResponseResultData.class);

        when(currentTransaction.getId()).thenReturn(ID);
        when(brainTreePaymentService.cloneTransaction(any(BrainTreeCloneTransactionRequest.class))).thenReturn(result);
        when(brainTreeCustomerSupportResponseConverter.convert(result)).thenReturn(responseResultData);

        BrainTreeResponseResultData resultTest = unit.cloneTransaction(currentTransaction, AMOUNT, true);

        assertEquals(responseResultData, resultTest);

        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(currentTransaction).getId();
        verify(brainTreePaymentService).cloneTransaction(any(BrainTreeCloneTransactionRequest.class));
        verify(brainTreeCustomerSupportResponseConverter).convert(result);
    }

    @Test
    public void shouldNotCloneTransaction() {
        BrainTreeTransactionDetailModel currentTransaction = mock(BrainTreeTransactionDetailModel.class);

        when(user.getUid()).thenReturn(null);

        unit.cloneTransaction(currentTransaction, AMOUNT, true);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldCreateCreditCardPaymentMethod() {
        BrainTreePaymentMethodResult result = mock(BrainTreePaymentMethodResult.class);
        PaymentMethod paymentMethod = mock(PaymentMethod.class);
        BrainTreePaymentInfoModel paymentInfo = mock(BrainTreePaymentInfoModel.class);

        when(brainTreePaymentService.createCreditCardPaymentMethod(any(BrainTreeCreateCreditCardPaymentMethodRequest.class))).thenReturn(result);
        when(result.isSuccess()).thenReturn(true);
        when(result.getPaymentMethod()).thenReturn(paymentMethod);
        when(paymentMethodConverter.convert(paymentMethod)).thenReturn(paymentInfo);
        when(result.getCardholderName()).thenReturn(CARD_HOLDER_NAME);
        doNothing().when(paymentInfoService).addToCustomer(paymentInfo);

        BrainTreePaymentMethodResult resultTest = unit.createCreditCardPaymentMethod(ID, PAYMENT_METHOD_NONCE, CARD_HOLDER_NAME, true, BILLING_ADDRESS_ID);

        assertEquals(result, resultTest);

        verify(brainTreePaymentService).createCreditCardPaymentMethod(createCreditCardPaymentMethodRequestCaptor.capture());
        BrainTreeCreateCreditCardPaymentMethodRequest request = createCreditCardPaymentMethodRequestCaptor.getValue();

        assertEquals(PAYMENT_METHOD_NONCE, request.getMethodNonce());
        assertEquals(ID, request.getCustomerId());
        assertEquals(CARD_HOLDER_NAME, request.getCardholderName());
        assertEquals(true, request.getIsDefault());
        assertEquals(BILLING_ADDRESS_ID, request.getBillingAddressId());

        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(result).isSuccess();
        verify(result, times(2)).getPaymentMethod();
        verify(paymentMethodConverter).convert(paymentMethod);
        verify(result).getCardholderName();
        verify(paymentInfoService).addToCustomer(paymentInfo);
    }

    @Test
    public void shouldDoNothingWhenCreateCreditCardPaymentMethodWithNullValues() {
        BrainTreePaymentMethodResult result = mock(BrainTreePaymentMethodResult.class);

        when(brainTreePaymentService.createCreditCardPaymentMethod(any(BrainTreeCreateCreditCardPaymentMethodRequest.class))).thenReturn(result);
        when(result.isSuccess()).thenReturn(true);
        when(result.getPaymentMethod()).thenReturn(null);

        unit.createCreditCardPaymentMethod(ID, PAYMENT_METHOD_NONCE, CARD_HOLDER_NAME, true, null);


        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(result).isSuccess();
        verify(paymentMethodGraphQLConverter, never()).convert(result);
        verify(result, never()).getCardholderName();
        verify(paymentInfoService, never()).addToCustomer(any());
    }

    @Test
    public void shouldNoCreateCreditCardPaymentMethod() {
        when(user.getUid()).thenReturn(null);

        BrainTreePaymentMethodResult result = unit.createCreditCardPaymentMethod(ID, PAYMENT_METHOD_NONCE, CARD_HOLDER_NAME, true, BILLING_ADDRESS_ID);

        assertEquals(null, result);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

    @Test
    public void shouldRemoveCustomer() {
        BrainTreeCustomerResult result = mock(BrainTreeCustomerResult.class);

        when(brainTreePaymentService.removeCustomer(any(BrainTreeCustomerRequest.class))).thenReturn(result);

        BrainTreeResponseResultData resultTest = unit.removeCustomer(ID);

        verify(userService).getCurrentUser();
        verify(user).getUid();
        verify(brainTreePaymentService).removeCustomer(any(BrainTreeCustomerRequest.class));
        verify(result).isSuccess();
        verify(result).getErrorMessage();
        verify(result).getErrorCode();
    }

    @Test
    public void shouldCreateTransaction() {
        BrainTreeTransactionInfo transactionInfo = mock(BrainTreeTransactionInfo.class);
        Currency currency = Currency.getInstance("USD");
        BigDecimal amount = mock(BigDecimal.class);
        BigDecimal tax = mock(BigDecimal.class);
        Map<String, String> customFields = mock(HashMap.class);
        BrainTreeSaleTransactionResult result = mock(BrainTreeSaleTransactionResult.class);
        BrainTreeResponseResultData brainTreeResponseResultData = mock(BrainTreeResponseResultData.class);

        when(transactionInfo.getCardHolder()).thenReturn(CARD_HOLDER_NAME);
        when(transactionInfo.getCardNumber()).thenReturn(CARD_NUMBER);
        when(transactionInfo.getCvv()).thenReturn(CVV);
        when(transactionInfo.getExpirationDate()).thenReturn(EXPIRATION_DATE);
        when(transactionInfo.getFirstName()).thenReturn(FIRST_NAME);
        when(transactionInfo.getLastName()).thenReturn(LAST_NAME);
        when(transactionInfo.getEmail()).thenReturn(EMAIL);
        when(transactionInfo.getBillingPostCode()).thenReturn(POST_CODE);
        when(transactionInfo.getBillingAddress()).thenReturn(BILLING_ADDRESS_ID);

        when(transactionInfo.getCurrency()).thenReturn(currency);
        when(transactionInfo.getAmount()).thenReturn(amount);

        when(transactionInfo.getShippingPostCode()).thenReturn(POST_CODE);
        when(transactionInfo.getShippingAddress()).thenReturn(STREET);

        when(transactionInfo.isPaymentMethodToken()).thenReturn(true);
        when(transactionInfo.getPaymentMethodToken()).thenReturn(TOKEN);
        when(transactionInfo.getTax()).thenReturn(tax);
        when(transactionInfo.getCustom()).thenReturn(customFields);
        when(transactionInfo.getEmail()).thenReturn(EMAIL);
        when(transactionInfo.getFirstName()).thenReturn(FIRST_NAME);
        when(transactionInfo.getLastName()).thenReturn(LAST_NAME);
        when(transactionInfo.getNonce()).thenReturn(PAYMENT_METHOD_NONCE);
        when(transactionInfo.getCardHolder()).thenReturn(CARD_HOLDER_NAME);

        when(transactionInfo.getMerchantAccountId()).thenReturn(BRAINTREE_ID);
        when(brainTreePaymentService.saleTransaction(any(BrainTreeSaleTransactionRequest.class))).thenReturn(result);
        when(brainTreeCustomerSupportResponseConverter.convert(result)).thenReturn(brainTreeResponseResultData);

        BrainTreeResponseResultData resultTest = unit.createTransaction(transactionInfo);

        assertEquals(brainTreeResponseResultData, resultTest);

        verify(brainTreePaymentService).saleTransaction(saleTransactionRequestCaptor.capture());
        BrainTreeSaleTransactionRequest value = saleTransactionRequestCaptor.getValue();

        assertEquals(CARD_HOLDER_NAME, value.getCard().getCardHolderFullName());
        assertEquals(CARD_NUMBER, value.getCard().getCardNumber());
        assertEquals(CVV, value.getCard().getCv2Number());
        assertEquals(1, value.getCard().getExpirationMonth().intValue());
        assertEquals(30, value.getCard().getExpirationYear().intValue());
        assertEquals(FIRST_NAME, value.getCard().getBillingInfo().getFirstName());
        assertEquals(LAST_NAME, value.getCard().getBillingInfo().getLastName());
        assertEquals(EMAIL, value.getCard().getBillingInfo().getEmail());
        assertEquals(POST_CODE, value.getCard().getBillingInfo().getPostalCode());
        assertEquals(BILLING_ADDRESS_ID, value.getCard().getBillingInfo().getStreet1());

        assertEquals(currency, value.getCurrency());
        assertEquals(amount, value.getTotalAmount());

        assertEquals(FIRST_NAME, value.getShippingInfo().getFirstName());
        assertEquals(LAST_NAME, value.getShippingInfo().getLastName());
        assertEquals(EMAIL, value.getShippingInfo().getEmail());
        assertEquals(POST_CODE, value.getShippingInfo().getPostalCode());
        assertEquals(STREET, value.getShippingInfo().getStreet1());

        assertEquals(true, value.getUsePaymentMethodToken());
        assertEquals(TOKEN, value.getPaymentMethodToken());
        assertEquals(tax, value.getTaxAmount());
        assertEquals(customFields, value.getCustomFields());
        assertEquals(EMAIL, value.getCustomerEmail());
        assertEquals(FIRST_NAME, value.getCustomerFirstName());
        assertEquals(LAST_NAME, value.getCustomerLastName());
        assertEquals(PAYMENT_METHOD_NONCE, value.getMethodNonce());
        assertEquals(CARD_HOLDER_NAME, value.getCardholderName());

        assertEquals(BRAINTREE_ID, value.getMerchantAccountIdForCurrentSite());

        verify(brainTreeCustomerSupportResponseConverter).convert(result);
    }

    @Test
    public void shouldNotCreateTransaction() {
        BrainTreeTransactionInfo brainTreeTransactionInfo = mock(BrainTreeTransactionInfo.class);

        when(user.getUid()).thenReturn(null);

        unit.createTransaction(brainTreeTransactionInfo);

        verify(userService).getCurrentUser();
        verify(user).getUid();
    }

}
