package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeAuthorizationRequest;
import com.braintree.command.result.BrainTreeAuthorizationResult;
import com.braintree.command.result.BrainTreePaymentDetailsResult;
import com.braintree.commands.impl.DefaultBrainTreeLoggingHandler;
import com.braintree.commands.impl.DefaultBraintreeCodeTranslator;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeAddressInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeChargeCreditCardInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeChargePaymentMethodInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeChargeVenmoAccountInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeRiskDataInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeTransactionDescriptorInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeTransactionInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeTransactionShippingInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeTransactionTaxInput;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentStatus;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import com.braintree.service.impl.DefaultBrainTreeDescriptorService;
import com.braintree.util.BrainTreeUtils;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.core.model.BraintreeGeneralConfigurationModel;
import de.hybris.platform.core.model.BraintreePluginConfigurationModel;
import de.hybris.platform.payment.commands.result.AuthorizationResult;
import de.hybris.platform.payment.dto.AvsStatus;
import de.hybris.platform.payment.dto.BillingInfo;
import de.hybris.platform.payment.dto.CvnStatus;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import de.hybris.platform.servicelayer.dto.converter.Converter;
import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;

import static com.braintree.enums.BrainTreePaymentMethod.BRAINTREEPAYPALEXPRESS;
import static com.braintree.enums.BrainTreePaymentMethod.VENMOACCOUNT;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String CUSTOMER_ID = "123456";
    private static final String PURCHASE_ORDER_NUMBER = "111111";
    private static final String SHIPS_FROM_POSTAL_CODE = "ships_from_postal_code";
    private static final String PHONE_NUMBER = "1234567898";
    private static final String BUSINESS_NAME = "name";
    private static final String URL = "url";
    private static final String SHIPPING_POSTAL_CODE = "78072";
    private static final String METHOD_NONCE = "method_nonce";
    private static final String VENMO_PROFILE_ID = "696996";
    private static final String CREDIT_CARD = "CreditCard";
    private static final String PAYPAL_ACCOUNT = "PayPalAccount";
    private static final String BILLING_LEGACY_ID = "5pyb6wm";
    private static final String TRANSACTION_LEGACY_ID = "1re22aqj";
    private static final String BILLING_ID = "cF5bWVudG1ldGhvZF9wcF81cHliNndt";
    private static final String TRANSACTION_ID = "dHJhbnNhY3Rpb25fMXJlMjJhcWo";
    private static final String AMOUNT = "12.44";
    private static final String CURRENCY_CODE = "USD";
    private static final String PAYPAL_TRANSACTION_DETAILS = "PayPalTransactionDetails";
    private static final String MERCHANT_ACCOUNT_ID = "MERCHANT_ACCOUNT_ID";
    private static final String STATUS_SETTLING = "SETTLING";
    private static final String ERROR_MESSAGE = "Unknown or expired single-use payment method.";
    private static final String ERROR_FULL_MESSAGE = "[BrainTree Validation Error] [code = 91565]";
    private static final String ERROR_LEGACY_CODE = "91565";
    private static final String DEFINITION_FILE_NAME_CHARGE_VENMO = "chargeVenmoAccount";
    private static final String DEFINITION_FILE_NAME_CHARGE = "chargePaymentMethod";
    private static final String DEFINITION_FILE_NAME_AUTHORIZATION_VENMO = "authorizeVenmoAccount";
    private static final String DEFINITION_FILE_NAME_AUTHORIZATION = "authorizePaymentMethod";
    private static final String DEFINITION_FILE_NAME_CHARGE_PAYPAL = "chargePayPalAccount";
    private static final String DEFINITION_FILE_NAME_AUTHORIZATION_PAYPAL = "authorizePayPalAccount";
    private static final String DEFINITION_FILE_NAME_CHARGE_CREDIT_CARD = "chargeCreditCard";
    private static final String DEFINITION_FILE_NAME_AUTHORIZATION_CREDIT_CARD = "authorizeCreditCard";
    private static final double AUTHORIZE_AMOUNT = 10.0;
    private static final String INPUT = "input";
    private static final String ERRORS = "errors";
    private static final String DATA = "data";
    private static final String TEST = "test";
    private static final String SETTLED_MSG = "The payment is in the process of being settled.";
    private final Calendar CREATED_AT = new GregorianCalendar();

    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;

    @Mock
    private DefaultBrainTreeDescriptorService descriptorService;

    @Mock
    private BrainTreeConfigService brainTreeConfigService;

    @Mock
    private BraintreePaymentInfoService paymentInfoService;

    @Mock
    protected ConfigurationService configurationService;

    @Mock
    private BrainTreePaymentService brainTreePaymentService;

    @Mock
    private DefaultBrainTreeLoggingHandler loggingHandler;

    @Mock
    private DefaultBraintreeCodeTranslator codeTranslator;

    @Mock
    private Converter<BillingInfo, BrainTreeAddressInput> braintreeBillingInfoToGraphQLAddressInputConverter;

    @Mock
    private Converter<BraintreePaymentMethodData, BrainTreePaymentDetailsResult> graphQLPaymentMethodToPaymentDetailsConverter;

    @Spy
    @InjectMocks
    private DefaultBrainTreeGraphQLAuthorizationCommand command;

    @Mock
    private BraintreePluginConfigurationModel braintreePluginConfiguration;

    @Mock
    private BraintreeGeneralConfigurationModel braintreeGeneralConfiguration;

    @Mock
    private BrainTreeAuthorizationRequest brainTreeAuthorizationRequest;

    @Mock
    private BillingInfo shippingInfo;

    @Before
    public void setUp() throws IOException {
        command.initMethod();

        Map<String, Object> result = new HashMap<>();
        result.put(TEST, TEST);

        when(brainTreeConfigService.getBrainTreePluginConfiguration()).thenReturn(braintreePluginConfiguration);
        when(braintreePluginConfiguration.getBraintreeGeneralConfiguration()).thenReturn(braintreeGeneralConfiguration);
        when(descriptorService.isDescriptorAvailable(CREDIT_CARD)).thenReturn(true);
        when(descriptorService.isDescriptorAvailable(BRAINTREEPAYPALEXPRESS.getCode())).thenReturn(true);
        when(descriptorService.isDescriptorAvailable(VENMOACCOUNT.getCode())).thenReturn(true);

        doReturn(result).when(command).makeGraphQlCall(any(), any());
        doReturn(StringUtils.EMPTY).when(command).parseToJson(any());
    }

    @Test
    public void shouldPerform() {

        doReturn(StringUtils.EMPTY).when(command).parseToJson(any());

        command.perform(brainTreeAuthorizationRequest);

    }

    @Test
    public void shouldUseChargeVenmoAccountFile() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(VENMOACCOUNT.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_VENMO), any());

    }

    @Test
    public void shouldUseAuthorizeVenmoAccountFile() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(VENMOACCOUNT.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_VENMO), any());

    }

    @Test
    public void shouldUseChargePayPalAccountFile() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_PAYPAL), any());

    }

    @Test
    public void shouldUseAuthorizePayPalAccountFile() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.PAYPAL.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_PAYPAL), any());

    }

    @Test
    public void shouldUseChargeCreditCardFileWhenGooglePayPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseAuthorizeCreditCardFileWhenGooglePayPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.ANDROIDPAYCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseChargeCreditCardFileWhenApplePayPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.APPLEPAYCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseAuthorizeCreditCardFileWhenApplePayPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.APPLEPAYCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseChargeCreditCardFileWhenSRCPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.VISACHECKOUTCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseAuthorizeCreditCardFileWhenSRCPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.VISACHECKOUTCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseChargeCreditCardFileWhenCCPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.CREDITCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseAuthorizeCreditCardFileWhenCCPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.CREDITCARD.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION_CREDIT_CARD), any());

    }

    @Test
    public void shouldUseChargePaymentMethodFileWhenDifferentPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.LOCALPAYMENT.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_CHARGE), any());

    }

    @Test
    public void shouldUseAuthorizePaymentMethodFileWhenDifferentPaymentType() throws IOException {

        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(com.braintree.enums.BrainTreePaymentMethod.USBANKACCOUNT.getCode());
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(false);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(eq(DEFINITION_FILE_NAME_AUTHORIZATION), any());

    }

    @Test
    public void checkVariablesMapForVenmo() throws IOException {

        Map<String, Object> variables = new HashMap<>();

        when(brainTreeAuthorizationRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeAuthorizationRequest.getTotalAmount()).thenReturn(BigDecimal.valueOf(10));

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(VENMOACCOUNT.getCode());
        when(brainTreeAuthorizationRequest.getVenmoProfileId()).thenReturn(VENMO_PROFILE_ID);

        BrainTreeChargePaymentMethodInput input = createBaseInput(new BrainTreeChargeVenmoAccountInput(), brainTreeAuthorizationRequest);

        BrainTreeTransactionDescriptorInput descriptorInput = getDescriptorTestData();
        when(descriptorService.getTransactionDescriptor(VENMOACCOUNT.getCode())).thenReturn(descriptorInput);
        input.getTransaction().setDescriptor(descriptorInput);

        variables.put(INPUT, input);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(any(), eq(variables));

    }

    @Test
    public void checkVariablesMapForPayPal() throws IOException {
        Map<String, Object> variables = new HashMap<>();

        when(brainTreeAuthorizationRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeAuthorizationRequest.getTotalAmount()).thenReturn(BigDecimal.valueOf(10));

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType())
                .thenReturn(BRAINTREEPAYPALEXPRESS.getCode());
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);

        BrainTreeChargePaymentMethodInput input = createBaseInput(new BrainTreeChargePaymentMethodInput(), brainTreeAuthorizationRequest);

        BrainTreeTransactionDescriptorInput descriptorInput = getDescriptorTestData();
        when(descriptorService.getTransactionDescriptor(BRAINTREEPAYPALEXPRESS.getCode())).thenReturn(descriptorInput);
        input.getTransaction().setDescriptor(descriptorInput);

        variables.put(INPUT, input);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(any(), eq(variables));

    }

    @Test
    public void checkVariablesMapForCreditCard() throws IOException {
        Map<String, Object> variables = new HashMap<>();

        when(brainTreeAuthorizationRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeAuthorizationRequest.getTotalAmount()).thenReturn(BigDecimal.valueOf(10));

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(CREDIT_CARD);
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);

        BrainTreeChargePaymentMethodInput input = createBaseInput(new BrainTreeChargeCreditCardInput(), brainTreeAuthorizationRequest);

        BrainTreeTransactionDescriptorInput descriptorInput = getDescriptorTestData();
        when(descriptorService.getTransactionDescriptor(CREDIT_CARD)).thenReturn(descriptorInput);
        input.getTransaction().setDescriptor(descriptorInput);

        variables.put(INPUT, input);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(any(), eq(variables));
    }

    @Test
    public void shouldApplyLevel2Level3DataWhenCreateVariablesMapForPayPal() throws IOException {
        Map<String, Object> variables = new HashMap<>();

        when(brainTreeAuthorizationRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeAuthorizationRequest.getTotalAmount()).thenReturn(BigDecimal.valueOf(10));
        when(brainTreeAuthorizationRequest.getTaxAmountAuthorize()).thenReturn(AUTHORIZE_AMOUNT);
        when(brainTreeAuthorizationRequest.getPurchaseOrderNumber()).thenReturn(PURCHASE_ORDER_NUMBER);

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType())
                .thenReturn(BRAINTREEPAYPALEXPRESS.getCode());
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);

        when(braintreeGeneralConfiguration.isLevel2Level3()).thenReturn(true);

        BrainTreeChargePaymentMethodInput input = createBaseInput(new BrainTreeChargePaymentMethodInput(), brainTreeAuthorizationRequest);
        BrainTreeTransactionTaxInput tax = new BrainTreeTransactionTaxInput();
        tax.setTaxAmount(BigDecimal.valueOf(AUTHORIZE_AMOUNT).setScale(2, RoundingMode.HALF_UP));
        input.getTransaction().setTax(tax);

        variables.put(INPUT, input);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(any(), eq(variables));
    }

    @Test
    public void shouldApplyLevel2Level3DataWhenCreateVariablesMapForCreditCard() throws IOException {
        Map<String, Object> variables = new HashMap<>();

        when(brainTreeAuthorizationRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(brainTreeAuthorizationRequest.getTotalAmount()).thenReturn(BigDecimal.valueOf(10));
        when(brainTreeAuthorizationRequest.getTaxAmountAuthorize()).thenReturn(AUTHORIZE_AMOUNT);
        when(brainTreeAuthorizationRequest.getPurchaseOrderNumber()).thenReturn(PURCHASE_ORDER_NUMBER);
        when(brainTreeAuthorizationRequest.getShippingAmount()).thenReturn(2.5);
        when(brainTreeAuthorizationRequest.getDiscountAmount()).thenReturn(0.5);
        when(brainTreeAuthorizationRequest.getShipsFromPostalCode()).thenReturn(SHIPS_FROM_POSTAL_CODE);
        when(brainTreeAuthorizationRequest.getShippingPostalCode()).thenReturn(SHIPPING_POSTAL_CODE);

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType()).thenReturn(CREDIT_CARD);
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);
        when(brainTreeAuthorizationRequest.getLineItems()).thenReturn(new ArrayList<>());
        when(brainTreeAuthorizationRequest.getShippingInfo()).thenReturn(shippingInfo);

        when(braintreeGeneralConfiguration.isLevel2Level3()).thenReturn(true);

        BrainTreeChargePaymentMethodInput input = createBaseInput(new BrainTreeChargeCreditCardInput(), brainTreeAuthorizationRequest);
        BrainTreeTransactionTaxInput tax = new BrainTreeTransactionTaxInput();
        tax.setTaxAmount(roundNumberToTwoDecimalPlaces(AUTHORIZE_AMOUNT));
        input.getTransaction().setTax(tax);

        input.getTransaction().getShipping().setShippingAmount("2.50");
        input.getTransaction().setDiscountAmount("0.50");
        input.getTransaction().getShipping().setShipsFromPostalCode(SHIPS_FROM_POSTAL_CODE);
        input.getTransaction().setLineItems(new ArrayList<>());

        BrainTreeAddressInput addressInput = new BrainTreeAddressInput();
        input.getTransaction().getShipping().setShippingAddress(addressInput);
        input.getTransaction().getShipping().getShippingAddress().setPostalCode(SHIPPING_POSTAL_CODE);

        when(braintreeBillingInfoToGraphQLAddressInputConverter.convert(shippingInfo)).thenReturn(addressInput);

        variables.put(INPUT, input);

        command.perform(brainTreeAuthorizationRequest);

        verify(command).makeGraphQlCall(any(), eq(variables));
    }

    @Test
    public void checkSuccessResult() throws IOException {
        BrainTreePaymentDetailsResult paymentDetailsResult = new BrainTreePaymentDetailsResult();

        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType())
                .thenReturn(BRAINTREEPAYPALEXPRESS.getCode());
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        Map<String, Object> value = createSuccessMap();

        final Map<String, Object> result = new HashMap<>();
        result.put(DATA, value);

        doReturn(result).when(command).makeGraphQlCall(any(), any());
        doReturn(paymentDetailsResult).when(graphQLPaymentMethodToPaymentDetailsConverter).convert(any());

        AuthorizationResult expectedAuthorizationResult = createSuccessResult(paymentDetailsResult, brainTreeAuthorizationRequest);

        AuthorizationResult actualAuthorizationResult = command.perform(brainTreeAuthorizationRequest);

        assertEquals(expectedAuthorizationResult, actualAuthorizationResult);

    }

    @Test
    public void checkFailedResult() throws IOException {
        when(brainTreeAuthorizationRequest.getUsePaymentMethodToken()).thenReturn(false);
        when(brainTreeAuthorizationRequest.getMethodNonce()).thenReturn(METHOD_NONCE);
        when(brainTreeAuthorizationRequest.getPaymentType())
                .thenReturn(BRAINTREEPAYPALEXPRESS.getCode());
        when(brainTreeAuthorizationRequest.getAdvancedFraudTools()).thenReturn(true);
        when(brainTreeAuthorizationRequest.getSubmitForSettlement()).thenReturn(true);

        ArrayList<Map<String, Object>> mapErrors = createFailedMap();
        Map<String, Object> chargePayPalAccount = new LinkedHashMap<>();
        chargePayPalAccount.put(DEFINITION_FILE_NAME_CHARGE_PAYPAL, null);

        final Map<String, Object> result = new HashMap<>();
        result.put(ERRORS, mapErrors);
        result.put(DATA, chargePayPalAccount);

        doReturn(result).when(command).makeGraphQlCall(any(), any());
        doReturn(TransactionStatusDetails.UNKNOWN_CODE).when(codeTranslator).translateReasonCode(ERROR_LEGACY_CODE);
        doReturn(ERROR_FULL_MESSAGE).when(loggingHandler).handleErrors(any());

        AuthorizationResult expectedAuthorizationResult = createFailedResult();

        AuthorizationResult actualAuthorizationResult = command.perform(brainTreeAuthorizationRequest);

        assertEquals(expectedAuthorizationResult, actualAuthorizationResult);

    }

    private BrainTreeChargePaymentMethodInput createBaseInput(BrainTreeChargePaymentMethodInput input,
                                                              BrainTreeAuthorizationRequest request) {
        BrainTreeTransactionInput transactionInput = new BrainTreeTransactionInput();
        BrainTreeTransactionShippingInput shippingInput = new BrainTreeTransactionShippingInput();
        BrainTreeRiskDataInput riskData = new BrainTreeRiskDataInput();


        transactionInput.setAmount(request.getTotalAmount());
        transactionInput.setShipping(shippingInput);

        input.setPaymentMethodId(request.getMethodNonce());
        input.setTransaction(transactionInput);

        if (BrainTreeUtils.isPayPalPayment(request.getPaymentType())) {
            riskData.setDeviceData(request.getDeviceData());
            input.getTransaction().setRiskData(riskData);
        }

        if (CREDIT_CARD.equals(request.getPaymentType())) {
            riskData.setDeviceData(request.getDeviceData());

            input.getTransaction().setRiskData(riskData);

        }

        return input;
    }

    private Map<String, Object> createSuccessMap() {
        Map<String, Object> value = new LinkedHashMap<>();
        Map<String, Object> chargePayPalAccount = new LinkedHashMap<>();
        Map<String, Object> transaction = new LinkedHashMap<>();
        Map<String, Object> amount = new LinkedHashMap<>();
        Map<String, Object> billingAgreementWithPurchasePaymentMethod = new LinkedHashMap<>();
        Map<String, Object> details = new LinkedHashMap<>();
        Map<String, Object> paymentMethodSnapshot = new LinkedHashMap<>();

        details.put("__typename", PAYPAL_ACCOUNT);
        billingAgreementWithPurchasePaymentMethod.put("details", details);
        billingAgreementWithPurchasePaymentMethod.put("legacyId", BILLING_LEGACY_ID);
        billingAgreementWithPurchasePaymentMethod.put("id", BILLING_ID);

        amount.put("value", AMOUNT);
        amount.put("currencyCode", CURRENCY_CODE);
        paymentMethodSnapshot.put("__typename", PAYPAL_TRANSACTION_DETAILS);
        transaction.put("id", TRANSACTION_ID);
        transaction.put("legacyId", TRANSACTION_LEGACY_ID);
        transaction.put("merchantAccountId", MERCHANT_ACCOUNT_ID);
        transaction.put("createdAt", toISO8601UTC(CREATED_AT.getTime()));
        transaction.put("status", STATUS_SETTLING);
        transaction.put("paymentMethodSnapshot", paymentMethodSnapshot);
        transaction.put("paymentMethod", null);
        transaction.put("amount", amount);

        chargePayPalAccount.put("transaction", transaction);
        chargePayPalAccount.put("billingAgreementWithPurchasePaymentMethod", billingAgreementWithPurchasePaymentMethod);

        value.put(DEFINITION_FILE_NAME_CHARGE_PAYPAL, chargePayPalAccount);

        return value;
    }

    private ArrayList<Map<String, Object>> createFailedMap() {
        ArrayList<Map<String, Object>> mapErrors = new ArrayList<>();
        Map<String, Object> value = new LinkedHashMap<>();
        Map<String, Object> extensions = new LinkedHashMap<>();

        value.put("message", ERROR_MESSAGE);
        extensions.put("legacyCode", ERROR_LEGACY_CODE);
        value.put("extensions", extensions);

        mapErrors.add(value);

        return mapErrors;
    }

    private BrainTreeAuthorizationResult createSuccessResult(BrainTreePaymentDetailsResult paymentDetailsResult,
                                                             BrainTreeAuthorizationRequest request) {
        BrainTreeAuthorizationResult authorizationResult = new BrainTreeAuthorizationResult();

        authorizationResult.setSuccess(true);
        authorizationResult.setPaymentDetails(paymentDetailsResult);
        authorizationResult.setRequestGraphQLId(TRANSACTION_ID);
        authorizationResult.setShouldGetMethodTokenFromBraintree(request.getUsePaymentMethodToken());
        authorizationResult.setRequestBody("");
        authorizationResult.setResponseBody("");
        authorizationResult.setCurrency(Currency.getInstance(CURRENCY_CODE));
        authorizationResult.setTotalAmount(new BigDecimal(AMOUNT));
        authorizationResult.setAvsStatus(AvsStatus.MATCHED);
        authorizationResult.setCvnStatus(CvnStatus.MATCHED);
        authorizationResult.setAuthorizationTime(CREATED_AT.getTime());
        authorizationResult.setMerchantTransactionCode(MERCHANT_ACCOUNT_ID);
        authorizationResult.setRequestId(TRANSACTION_LEGACY_ID);
        authorizationResult.setRequestToken(TRANSACTION_LEGACY_ID);
        authorizationResult.setTransactionStatus(TransactionStatus.ACCEPTED);
        authorizationResult.setTransactionStatusDetails(TransactionStatusDetails.SUCCESFULL);
        authorizationResult.setBtFraudCheckToolDecision(StringUtils.EMPTY);
        authorizationResult.setStatus(STATUS_SETTLING);
        authorizationResult.setMessage(SETTLED_MSG);

        return authorizationResult;
    }

    private BrainTreeAuthorizationResult createFailedResult() {
        BrainTreeAuthorizationResult authorizationResult = new BrainTreeAuthorizationResult();

        authorizationResult.setSuccess(false);
        authorizationResult.setShouldGetMethodTokenFromBraintree(false);
        authorizationResult.setRequestBody("");
        authorizationResult.setResponseBody("");
        authorizationResult.setMessage(ERROR_MESSAGE);
        authorizationResult.setAuthorizationTime(CREATED_AT.getTime());
        authorizationResult.setRequestId("N/A");
        authorizationResult.setTransactionStatus(TransactionStatus.REJECTED);
        authorizationResult.setTransactionStatusDetails(TransactionStatusDetails.UNKNOWN_CODE);
        authorizationResult.setBtFraudCheckToolDecision(StringUtils.EMPTY);
        authorizationResult.setStatus(BrainTreePaymentStatus.FAILED.name());

        return authorizationResult;
    }

    private static String toISO8601UTC(Date date) {
        TimeZone tz = TimeZone.getTimeZone("UTC");
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
        df.setTimeZone(tz);
        return df.format(date);
    }

    private BigDecimal roundNumberToTwoDecimalPlaces(final Double number) {
        return BigDecimal.valueOf(number).setScale(2, RoundingMode.HALF_UP);
    }

    private BrainTreeTransactionDescriptorInput getDescriptorTestData() {
        BrainTreeTransactionDescriptorInput descriptorInput = new BrainTreeTransactionDescriptorInput();
        descriptorInput.setName(BUSINESS_NAME);
        descriptorInput.setPhone(PHONE_NUMBER);
        descriptorInput.setUrl(URL);
        return descriptorInput;
    }
}
