package com.braintree.commands.impl;

import com.braintree.command.request.BrainTreeAddressRequest;
import com.braintree.command.request.BrainTreeAuthorizationRequest;
import com.braintree.command.request.BrainTreeCreatePaymentMethodRequest;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeCustomer;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeErrorDefinition;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeErrorExtension;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeMonetaryAmount;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodDetails;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodOrigin;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransaction;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransactionPayload;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeVaultPaymentMethodPayload;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeVerification;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeVerificationProcessorResponse;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintreegateway.Address;
import com.braintreegateway.AndroidPayCard;
import com.braintreegateway.AndroidPayDetails;
import com.braintreegateway.CreditCard;
import com.braintreegateway.CreditCardVerification;
import com.braintreegateway.Customer;
import com.braintreegateway.PayPalAccount;
import com.braintreegateway.Transaction;
import com.braintreegateway.UsBankAccount;
import com.braintreegateway.ValidationError;
import com.braintreegateway.ValidationErrorCode;
import com.braintreegateway.VenmoAccount;
import com.braintreegateway.VisaCheckoutCard;
import com.braintreegateway.VisaCheckoutCardDetails;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.payment.commands.request.CreateSubscriptionRequest;
import de.hybris.platform.payment.dto.BillingInfo;
import org.apache.log4j.Logger;
import org.junit.Assert;
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.math.BigDecimal;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import static com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentStatus.SETTLED;
import static com.braintreegateway.Transaction.Status.AUTHORIZED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

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

    public static final String AUTHORIZATION_REQUEST_TYPE = "[AUTHORIZATION REQUEST]";
    public static final String CUSTOMER_ID_NAME = "Customer ID";
    public static final String TOTAL_AMOUNT_NAME = "Total amount";
    private static final String POSTAL_CODE_RESPONSE_CODE = "123";
    private static final String STREET_ADDRESS_RESPONSE_CODE = "456";
    private static final String PROCESSOR_RESPONSE_CODE = "789";
    private static final String EMAIL = "email";
    private static final String PROCESSOR_RESPONSE_TEXT = "Processor declined the request";
    public static final String MOCK_CUSTOMER_ID = "customerId";
    private static final String ADDRESS_TYPE = "ADDRESS_TYPE";
    private static final String ADDRESS_ID_NAME = "Address Id";
    private static final String CARDHOLDER_NAME = "John Doe";
    private static final String CUSTOMER_ID = "customerId";
    private static final String CARD_TYPE = "Visa";
    private static final String CODE = "code";
    private static final String MASKED_NUMBER = "************1234";
    private static final String IMAGE_URL = "https://example.com/card_image.png";
    private static final String EXPIRATION_MONTH = "12";
    private static final String EXPIRATION_YEAR = "2024";
    private static final String TOKEN = "cardToken";
    private static final String ISSUING_BANK = "Example Bank";
    private static final String FIRST_NAME = "John";
    private static final String LAST_NAME = "Doe";
    private static final String PAYEE_EMAIL = "payee@example.com";
    private static final String COMPANY_NAME = "Company";
    private static final String COUNTRY_NAME = "Country";
    private static final String MOCK_ADDRESS_ID = "addressId";
    private static final String MOCK_COMPANY = "company";
    private static final String MOCK_COUNTRY = "US";
    private static final String STREET_ADDRESS = "123 Main St";
    private static final String EXTENDED_ADDRESS = "Apt 45";
    private static final String LOCALITY = "Cityville";
    private static final String REGION = "State";
    private static final String POSTAL_CODE = "12345";
    private static final String MERCHANT_ACCOUNT_ID = "merchantAccountId";
    private static final String LEGACY_ID = "legacyId";
    private static final String TRANSACTION_ID = "transactionId";
    private static final String TRANSACTION_TYPE = "TYPE";
    public static final BigDecimal MOCK_TOTAL_AMOUNT = new BigDecimal("100.00");
    private static final String METHOD_NONCE = "methodNonce";
    private static final String PAYPAL_EMAIL = "paypal@example.com";
    private static final String TYPE = "TYPE";
    private static final String CARD_NUMBER_LAST_4 = "1234";
    private static final String REASON = "reason";
    private static final String EXPECTED_CARD_DATA =
            "[SRC CARD]Card number = **************1234\n" +
            "[SRC CARD]Card type = Visa\n" +
            "[SRC CARD]Image url = https://example.com/card_image.png\n" +
            "[SRC CARD]Expiration = 12-2024\n" +
            "[SRC CARD]Token = cardToken\n";

    @Mock
    private BrainTreeErrorDefinition mockErrorDefinition;
    @Mock
    private AndroidPayCard mockAndroidPayCard;
    @Mock
    private CreditCardVerification mockVerification;
    @Mock
    private VisaCheckoutCard mockVisaCheckoutCardDetails;
    @Mock
    private VisaCheckoutCardDetails mockVisaCardDetails;
    @Mock
    private BrainTreeVaultPaymentMethodPayload mockVaultPayload;
    @Mock
    private BrainTreePaymentMethodDetails mockPaymentMethodDetails;
    @Mock
    private VenmoAccount mockVenmoAccount;
    @Mock
    private CreateSubscriptionRequest mockCreateSubscriptionRequest;
    @Mock
    private BillingInfo mockBillingInfo;
    @Mock
    private BrainTreeCustomer mockCustomer;
    @Mock
    private BraintreePaymentMethodData mockPaymentMethod;

    @Mock
    private UsBankAccount mockUsBankAccount;

    @Mock
    private PayPalAccount mockPayPalAccount;

    @Mock
    private BrainTreeCreatePaymentMethodRequest mockRequest;
    @Mock
    private BrainTreeAuthorizationRequest mockAuthorizationRequest;
    @Mock
    private BrainTreeTransactionPayload mockPayload;
    @Mock
    private BrainTreeTransaction mockTransaction;
    @Mock
    private CreditCard mockCreditCard;
    @Mock
    private Transaction transaction;
    @Mock
    private Address mockAddress;
    @Mock
    private ValidationError mockError;
    @Mock
    private BrainTreeAddressRequest mockAddressRequest;

    @Spy
    @InjectMocks
    DefaultBrainTreeLoggingHandler defaultBrainTreeLoggingHandler;


    @Test
    public void handleAuthorizationRequestShouldVerifyIfEmptyPassedArgumentsBeforeLogging() {
        when(mockAuthorizationRequest.getCustomerId()).thenReturn(MOCK_CUSTOMER_ID);
        when(mockAuthorizationRequest.getTotalAmount()).thenReturn(MOCK_TOTAL_AMOUNT);

        defaultBrainTreeLoggingHandler.handleAuthorizationRequest(mockAuthorizationRequest);

        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), eq(AUTHORIZATION_REQUEST_TYPE),
                eq(CUSTOMER_ID_NAME), eq(MOCK_CUSTOMER_ID));

        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), eq(AUTHORIZATION_REQUEST_TYPE),
                eq(TOTAL_AMOUNT_NAME), eq(MOCK_TOTAL_AMOUNT.toString()));
    }

    @Test
    public void handleAddressRequestShouldVerifyIfEmptyPassedArgumentsBeforeLogging() {
        when(mockAddressRequest.getAddressId()).thenReturn(MOCK_ADDRESS_ID);
        when(mockAddressRequest.getCompany()).thenReturn(MOCK_COMPANY);
        when(mockAddressRequest.getCountryCodeAlpha2()).thenReturn(MOCK_COUNTRY);

        defaultBrainTreeLoggingHandler.handleAddressRequest(ADDRESS_TYPE, mockAddressRequest);

        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), eq(ADDRESS_TYPE),
                eq(ADDRESS_ID_NAME), eq(MOCK_ADDRESS_ID));
        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), eq(ADDRESS_TYPE),
                eq(COMPANY_NAME), eq(MOCK_COMPANY));
        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), eq(ADDRESS_TYPE),
                eq(COUNTRY_NAME), eq(MOCK_COUNTRY));
    }

    @Test
    public void handleResultShouldFormatAddressBeforeLogging() {
        when(mockAddress.getFirstName()).thenReturn(FIRST_NAME);
        when(mockAddress.getLastName()).thenReturn(LAST_NAME);
        when(mockAddress.getStreetAddress()).thenReturn(STREET_ADDRESS);
        when(mockAddress.getExtendedAddress()).thenReturn(EXTENDED_ADDRESS);
        when(mockAddress.getLocality()).thenReturn(LOCALITY);
        when(mockAddress.getRegion()).thenReturn(REGION);
        when(mockAddress.getPostalCode()).thenReturn(POSTAL_CODE);
        when(mockAddress.getCountryName()).thenReturn(COUNTRY_NAME);

        try {
            defaultBrainTreeLoggingHandler.handleResult(ADDRESS_TYPE, mockAddress);
        } catch (Exception e) {
            Assert.fail("Address validation failed");
        }

        verify(mockAddress, times(1)).getFirstName();
        verify(mockAddress, times(1)).getLastName();
        verify(mockAddress, times(1)).getStreetAddress();
        verify(mockAddress, times(1)).getExtendedAddress();
        verify(mockAddress, times(1)).getLocality();
        verify(mockAddress, times(1)).getRegion();
        verify(mockAddress, times(1)).getPostalCode();
        verify(mockAddress, times(1)).getCountryName();
    }

    @Test
    public void handleResultShouldFormatBraintreeTransactionBeforeLogging() {
        BrainTreeMonetaryAmount mockAmount = new BrainTreeMonetaryAmount();
        when(mockTransaction.getLegacyId()).thenReturn(LEGACY_ID);
        when(mockTransaction.getId()).thenReturn(TRANSACTION_ID);
        when(mockTransaction.getAmount()).thenReturn(mockAmount);
        when(mockTransaction.getStatus()).thenReturn(SETTLED);

        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, mockTransaction);

        verify(defaultBrainTreeLoggingHandler, times(1)).formatTransaction(TRANSACTION_TYPE, mockTransaction);
    }

    @Test
    public void handleResultShouldNotFormatWhenBraintreeTransactionIsNullBeforeLogging() {
        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, (BrainTreeTransaction) null);

        verify(defaultBrainTreeLoggingHandler, never()).formatTransaction(TRANSACTION_TYPE, mockTransaction);
    }

    @Test
    public void handleResultShouldFormatTransactionBeforeLogging() {
        when(transaction.getId()).thenReturn(LEGACY_ID);
        when(transaction.getMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        when(transaction.getId()).thenReturn(TRANSACTION_ID);
        when(transaction.getAmount()).thenReturn(MOCK_TOTAL_AMOUNT);
        when(transaction.getStatus()).thenReturn(AUTHORIZED);
        when(transaction.getUpdatedAt()).thenReturn(Calendar.getInstance());

        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, transaction);

        verify(defaultBrainTreeLoggingHandler, times(1)).formatTransaction(TRANSACTION_TYPE, transaction);
    }

    @Test
    public void handleResultShouldFormatAndroidPayTransactionBeforeLogging() {
        AndroidPayDetails androidPayDetails = mock(AndroidPayDetails.class);

        when(transaction.getId()).thenReturn(LEGACY_ID);
        when(transaction.getMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        when(transaction.getId()).thenReturn(TRANSACTION_ID);
        when(transaction.getAmount()).thenReturn(MOCK_TOTAL_AMOUNT);
        when(transaction.getStatus()).thenReturn(AUTHORIZED);
        when(transaction.getUpdatedAt()).thenReturn(Calendar.getInstance());
        when(transaction.getAndroidPayDetails()).thenReturn(androidPayDetails);

        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, transaction);

        verify(defaultBrainTreeLoggingHandler, times(1)).formatTransaction(TRANSACTION_TYPE, transaction);
    }

    @Test
    public void handleResultShouldFormatVisaTransactionBeforeLogging() {
        VisaCheckoutCardDetails details = mock(VisaCheckoutCardDetails.class);

        when(transaction.getId()).thenReturn(LEGACY_ID);
        when(transaction.getMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        when(transaction.getId()).thenReturn(TRANSACTION_ID);
        when(transaction.getAmount()).thenReturn(MOCK_TOTAL_AMOUNT);
        when(transaction.getStatus()).thenReturn(AUTHORIZED);
        when(transaction.getUpdatedAt()).thenReturn(Calendar.getInstance());
        when(transaction.getVisaCheckoutCardDetails()).thenReturn(details);

        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, transaction);

        verify(defaultBrainTreeLoggingHandler, times(1)).formatTransaction(TRANSACTION_TYPE, transaction);
    }

    @Test
    public void handleResultShouldFormatTransactionNull() {
        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, (Transaction) null);

        verify(defaultBrainTreeLoggingHandler, never()).formatTransaction(TRANSACTION_TYPE, transaction);
    }

    @Test
    public void handleResultShouldFormatTransactionPayloadBeforeLogging() {
        Calendar createdAt = Calendar.getInstance();
        when(mockPayload.getTransaction()).thenReturn(mockTransaction);
        when(mockTransaction.getMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        when(mockTransaction.getLegacyId()).thenReturn(LEGACY_ID);
        when(mockTransaction.getId()).thenReturn(TRANSACTION_ID);
        when(mockTransaction.getAmount()).thenReturn(new BrainTreeMonetaryAmount());
        when(mockTransaction.getCreatedAt()).thenReturn(createdAt);
        when(mockTransaction.getStatus()).thenReturn(SETTLED);

        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, mockPayload);

        verify(defaultBrainTreeLoggingHandler, times(1)).formatTransaction(TRANSACTION_TYPE, mockPayload);
    }

    @Test
    public void handleResultShouldNotFormatWhenTransactionPayloadIsNullBeforeLogging() {
        defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, (BrainTreeTransactionPayload) null);

        verify(defaultBrainTreeLoggingHandler, never()).formatTransaction(TRANSACTION_TYPE, mockPayload);
    }

    @Test
    public void handleCreatePaymentMethodRequestShouldVerifyEmptyFields() {
        when(mockRequest.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockRequest.getMethodNonce()).thenReturn(METHOD_NONCE);

        defaultBrainTreeLoggingHandler.handleCreatePaymentMethodRequest(mockRequest);

        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(any(StringBuilder.class), eq("[PAYMENT METHOD REQUEST]"), eq("Customer ID"), eq(CUSTOMER_ID));
        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(any(StringBuilder.class), eq("[PAYMENT METHOD REQUEST]"), eq("Method nonce"), eq(METHOD_NONCE));
        verify(mockRequest).getCustomerId();
        verify(mockRequest).getMethodNonce();
    }

    @Test
    public void handleResultShouldFormatCreditCardBeforeLogging() {
        when(mockCreditCard.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(mockCreditCard.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockCreditCard.getCardType()).thenReturn(CARD_TYPE);
        when(mockCreditCard.getMaskedNumber()).thenReturn(MASKED_NUMBER);
        when(mockCreditCard.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockCreditCard.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(mockCreditCard.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(mockCreditCard.getToken()).thenReturn(TOKEN);
        when(mockCreditCard.getIssuingBank()).thenReturn(ISSUING_BANK);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, mockCreditCard);
        } catch (Exception e) {
            Assert.fail("Card validation failed");
        }

        verify(mockCreditCard, times(1)).getCardholderName();
        verify(mockCreditCard, times(1)).getCardType();
        verify(mockCreditCard, times(1)).getCustomerId();
        verify(mockCreditCard, times(1)).getMaskedNumber();
        verify(mockCreditCard, times(1)).getImageUrl();
        verify(mockCreditCard, times(1)).getIssuingBank();
        verify(mockCreditCard, times(1)).getToken();
        verify(mockCreditCard, times(1)).getExpirationYear();
    }

    @Test
    public void handleResultShouldFormatPayPalAccountBeforeLogging() {
        when(mockPayPalAccount.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockPayPalAccount.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockPayPalAccount.getToken()).thenReturn(TOKEN);
        when(mockPayPalAccount.getEmail()).thenReturn(PAYPAL_EMAIL);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, mockPayPalAccount);
        } catch (Exception e) {
            Assert.fail("PayPal validation failed");
        }

        verify(mockPayPalAccount, times(1)).getCustomerId();
        verify(mockPayPalAccount, times(1)).getImageUrl();
        verify(mockPayPalAccount, times(1)).getToken();
        verify(mockPayPalAccount, times(1)).getEmail();
    }

    @Test
    public void handleResultShouldNotFormatPayPalAccountBeforeLoggingWithNull() {
        try {
            defaultBrainTreeLoggingHandler.handleResult(TRANSACTION_TYPE, (PayPalAccount) null);
        } catch (Exception e) {
            Assert.fail("PayPal validation failed");
        }

        verify(mockPayPalAccount, never()).getCustomerId();
        verify(mockPayPalAccount, never()).getImageUrl();
    }

    @Test
    public void testFormatVenmoBeforeLogging() {
        when(mockVenmoAccount.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockVenmoAccount.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockVenmoAccount.getToken()).thenReturn(TOKEN);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, mockVenmoAccount);
        } catch (Exception e) {
            Assert.fail("Venmo validation failed");
        }

        verify(mockVenmoAccount).getCustomerId();
        verify(mockVenmoAccount).getImageUrl();
        verify(mockVenmoAccount).getToken();
    }

    @Test
    public void testFormatVenmoBeforeLoggingWithNull() {
        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, (VenmoAccount) null);
        } catch (Exception e) {
            Assert.fail("Venmo validation failed");
        }

        verify(mockVenmoAccount, never()).getCustomerId();
        verify(mockVenmoAccount, never()).getImageUrl();
        verify(mockVenmoAccount, never()).getToken();
    }

    @Test
    public void testFormatUsBankAccountCardBeforeLogging() {
        when(mockUsBankAccount.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockUsBankAccount.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockUsBankAccount.getToken()).thenReturn(TOKEN);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, mockUsBankAccount);
        } catch (Exception e) {
            Assert.fail("ACH validation failed");
        }

        verify(mockUsBankAccount).getCustomerId();
        verify(mockUsBankAccount).getImageUrl();
        verify(mockUsBankAccount).getToken();
    }

    @Test
    public void testFormatUsBankAccountCardBeforeLoggingWithNull() {
        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, (UsBankAccount) null);
        } catch (Exception e) {
            Assert.fail("ACH validation failed");
        }

        verify(mockUsBankAccount, never()).getCustomerId();
        verify(mockUsBankAccount, never()).getImageUrl();
        verify(mockUsBankAccount, never()).getToken();
    }

    @Test
    public void testHandleErrorsWithNoEmptyList() {
        ValidationError error = mock(ValidationError.class);
        List<ValidationError> errorList = Arrays.asList(error);

        when(error.getCode()).thenReturn(ValidationErrorCode.UNKNOWN_VALIDATION_ERROR);

        String result = defaultBrainTreeLoggingHandler.handleValidationErrors(errorList);

        assertNotNull(result);
        verify(error, times(4)).getCode();
    }

    @Test
    public void testFormatAndroidPayCardBeforeLogging() {
        when(mockAndroidPayCard.getCustomerId()).thenReturn(CUSTOMER_ID);
        when(mockAndroidPayCard.getSourceCardType()).thenReturn(CARD_TYPE);
        when(mockAndroidPayCard.getSourceCardLast4()).thenReturn(CARD_NUMBER_LAST_4);
        when(mockAndroidPayCard.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockAndroidPayCard.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(mockAndroidPayCard.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(mockAndroidPayCard.getToken()).thenReturn(TOKEN);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, mockAndroidPayCard);
        } catch (Exception e) {
            Assert.fail("ACH validation failed");
        }

        verify(mockAndroidPayCard).getCustomerId();
        verify(mockAndroidPayCard).getSourceCardType();
        verify(mockAndroidPayCard).getSourceCardLast4();
        verify(mockAndroidPayCard).getImageUrl();
        verify(mockAndroidPayCard).getExpirationMonth();
        verify(mockAndroidPayCard).getExpirationYear();
        verify(mockAndroidPayCard).getToken();
    }

    @Test
    public void testShouldNotFormatWhenAndroidPayCardIsNullBeforeLogging() {
        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, (AndroidPayCard) null);
        } catch (Exception e) {
            Assert.fail("ACH validation failed");
        }

        verify(mockAndroidPayCard, never()).getCustomerId();
        verify(mockAndroidPayCard, never()).getSourceCardType();
        verify(mockAndroidPayCard, never()).getSourceCardLast4();
    }

    @Test
    public void testFormatSrcCardBeforeLogging() {
        when(mockVisaCheckoutCardDetails.getLast4()).thenReturn(CARD_NUMBER_LAST_4);
        when(mockVisaCheckoutCardDetails.getCardType()).thenReturn(CARD_TYPE);
        when(mockVisaCheckoutCardDetails.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockVisaCheckoutCardDetails.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(mockVisaCheckoutCardDetails.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(mockVisaCheckoutCardDetails.getToken()).thenReturn(TOKEN);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, mockVisaCheckoutCardDetails);
        } catch (Exception e) {
            Assert.fail("SRC validation failed");
        }

        verify(mockVisaCheckoutCardDetails).getLast4();
        verify(mockVisaCheckoutCardDetails).getCardType();
        verify(mockVisaCheckoutCardDetails).getImageUrl();
        verify(mockVisaCheckoutCardDetails).getExpirationMonth();
        verify(mockVisaCheckoutCardDetails).getExpirationYear();
        verify(mockVisaCheckoutCardDetails).getToken();
    }

    @Test
    public void testShouldNotFormatSrcWhenCardIsNullBeforeLogging() {
        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, (VisaCheckoutCard) null);
        } catch (Exception e) {
            Assert.fail("SRC validation failed");
        }

        verify(mockVisaCheckoutCardDetails, never()).getLast4();
        verify(mockVisaCheckoutCardDetails, never()).getCardType();
        verify(mockVisaCheckoutCardDetails, never()).getImageUrl();
        verify(mockVisaCheckoutCardDetails, never()).getExpirationMonth();
        verify(mockVisaCheckoutCardDetails, never()).getExpirationYear();
        verify(mockVisaCheckoutCardDetails, never()).getToken();
    }

    @Test
    public void handleErrorsBeforeLoggingShouldReturnEmptyString() {
        List<ValidationError> errors = Collections.emptyList();

        String result = defaultBrainTreeLoggingHandler.handleValidationErrors(errors);

        assertTrue(result.isEmpty());
    }

    @Test
    public void testHandleErrorsDefinitionBeforeLogging() {
        BrainTreeErrorExtension errorExtension = mock(BrainTreeErrorExtension.class);
        when(mockErrorDefinition.getMessage()).thenReturn("Validation Error Message");
        when(mockErrorDefinition.getExtensions()).thenReturn(errorExtension);
        List<BrainTreeErrorDefinition> errors = Arrays.asList(mockErrorDefinition, mockErrorDefinition, mockErrorDefinition);

        String result = defaultBrainTreeLoggingHandler.handleErrors(errors);

        assertNotNull(result);
        verify(mockErrorDefinition, times(4)).getMessage();
    }

    @Test
    public void handleErrorsDefinitionBeforeLoggingShouldReturnEmptyString() {
        List<BrainTreeErrorDefinition> errors = Collections.emptyList();

        String result = defaultBrainTreeLoggingHandler.handleErrors(errors);

        assertTrue(result.isEmpty());
        verifyZeroInteractions(mockError);
    }

    @Test
    public void testHandleCardVerificationError() {
        when(mockVerification.getAvsPostalCodeResponseCode()).thenReturn(POSTAL_CODE_RESPONSE_CODE);
        when(mockVerification.getAvsStreetAddressResponseCode()).thenReturn(STREET_ADDRESS_RESPONSE_CODE);
        when(mockVerification.getStatus()).thenReturn(CreditCardVerification.Status.GATEWAY_REJECTED);
        when(mockVerification.getProcessorResponseCode()).thenReturn(PROCESSOR_RESPONSE_CODE);
        when(mockVerification.getProcessorResponseText()).thenReturn(PROCESSOR_RESPONSE_TEXT);

        try {
            defaultBrainTreeLoggingHandler.handleCardVerificationError(mockVerification);
        } catch (Exception e) {
            Assert.fail("Card validation failed");
        }

        verify(mockVerification, times(3)).getStatus();
        verify(mockVerification).getAvsPostalCodeResponseCode();
        verify(mockVerification).getAvsStreetAddressResponseCode();
        verify(mockVerification).getProcessorResponseCode();
        verify(mockVerification).getProcessorResponseText();
    }

    @Test
    public void testHandleCardVerificationErrorWithProcessorDeclined() {
        when(mockVerification.getAvsPostalCodeResponseCode()).thenReturn(POSTAL_CODE_RESPONSE_CODE);
        when(mockVerification.getAvsStreetAddressResponseCode()).thenReturn(STREET_ADDRESS_RESPONSE_CODE);
        when(mockVerification.getStatus()).thenReturn(CreditCardVerification.Status.PROCESSOR_DECLINED);
        when(mockVerification.getProcessorResponseCode()).thenReturn(PROCESSOR_RESPONSE_CODE);
        when(mockVerification.getProcessorResponseText()).thenReturn(PROCESSOR_RESPONSE_TEXT);

        try {
            defaultBrainTreeLoggingHandler.handleCardVerificationError(mockVerification);
        } catch (Exception e) {
            Assert.fail("Card validation failed");
        }

        verify(mockVerification, times(3)).getStatus();
        verify(mockVerification).getAvsPostalCodeResponseCode();
        verify(mockVerification).getAvsStreetAddressResponseCode();
        verify(mockVerification).getProcessorResponseCode();
        verify(mockVerification).getProcessorResponseText();
    }

    @Test
    public void testHandleResultForCustomer() {
        Customer mockCustomer = mock(Customer.class);
        when(mockCustomer.getId()).thenReturn(CUSTOMER_ID);
        when(mockCustomer.getFirstName()).thenReturn(FIRST_NAME);
        when(mockCustomer.getLastName()).thenReturn(LAST_NAME);
        when(mockCustomer.getEmail()).thenReturn(EMAIL);

        try {
            defaultBrainTreeLoggingHandler.handleResult(TYPE, mockCustomer);
        } catch (Exception e) {
            Assert.fail("Customer validation failed");
        }

        verify(mockCustomer).getEmail();
        verify(mockCustomer).getFirstName();
        verify(mockCustomer).getLastName();
        verify(mockCustomer).getId();
    }

    @Test
    public void testHandleCreateSubscriptionRequest() {
        when(mockCreateSubscriptionRequest.getBillingInfo()).thenReturn(mockBillingInfo);
        when(mockBillingInfo.getFirstName()).thenReturn(FIRST_NAME);
        when(mockBillingInfo.getLastName()).thenReturn(LAST_NAME);
        when(mockBillingInfo.getEmail()).thenReturn(EMAIL);

        defaultBrainTreeLoggingHandler.handleCreateSubscriptionRequest(mockCreateSubscriptionRequest, PAYEE_EMAIL);

        verify(mockCreateSubscriptionRequest, times(1)).getBillingInfo();
        verify(mockBillingInfo, times(1)).getFirstName();
        verify(mockBillingInfo, times(1)).getLastName();
        verify(mockBillingInfo, times(1)).getEmail();
    }

    @Test
    public void testFormatTransaction() {
        BrainTreePaymentMethodDetails brainTreePaymentMethodDetails = mock(BrainTreePaymentMethodDetails.class);
        BrainTreePaymentMethodOrigin origin = mock(BrainTreePaymentMethodOrigin.class);

        when(mockPayload.getTransaction()).thenReturn(mockTransaction);
        when(mockTransaction.getPaymentMethod()).thenReturn(mockPaymentMethod);
        when(mockTransaction.getMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        when(mockTransaction.getAmount()).thenReturn(new BrainTreeMonetaryAmount());
        when(mockTransaction.getCreatedAt()).thenReturn(Calendar.getInstance());
        when(mockPaymentMethod.getId()).thenReturn(TRANSACTION_ID);
        when(mockPaymentMethod.getLegacyId()).thenReturn(LEGACY_ID);
        when(mockPaymentMethod.getDetails()).thenReturn(brainTreePaymentMethodDetails);
        when(brainTreePaymentMethodDetails.getOrigin()).thenReturn(origin);
        when(origin.getType()).thenReturn(TYPE);
        when(brainTreePaymentMethodDetails.getLast4()).thenReturn(CARD_NUMBER_LAST_4);
        when(brainTreePaymentMethodDetails.getBrandCode()).thenReturn(CODE);
        when(brainTreePaymentMethodDetails.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(brainTreePaymentMethodDetails.getExpirationYear()).thenReturn(EXPIRATION_YEAR);

        String formattedTransaction = defaultBrainTreeLoggingHandler.formatTransaction(TYPE, mockPayload);

        assertNotNull(formattedTransaction);
        verify(mockPaymentMethod, times(2)).getId();
        verify(mockTransaction).getMerchantAccountId();
    }



    @Test
    public void testHandleResultWithVaultPaymentMethodPayload() {
        // Arrange
        when(mockVaultPayload.getPaymentMethod()).thenReturn(mockPaymentMethod);
        when(mockPaymentMethod.getDetails()).thenReturn(mockPaymentMethodDetails);
        when(mockPaymentMethodDetails.get__typename()).thenReturn(CARD_TYPE);
        when(mockPaymentMethod.getId()).thenReturn(CUSTOMER_ID);
        when(mockPaymentMethod.getLegacyId()).thenReturn(LEGACY_ID);
        when(mockPaymentMethodDetails.getEmail()).thenReturn(EMAIL);
        when(mockPaymentMethodDetails.getBrandCode()).thenReturn(CARD_TYPE);
        when(mockPaymentMethodDetails.getCardholderName()).thenReturn(CARDHOLDER_NAME);
        when(mockPaymentMethodDetails.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(mockPaymentMethodDetails.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(mockPaymentMethodDetails.getLast4()).thenReturn(CARD_NUMBER_LAST_4);

        defaultBrainTreeLoggingHandler.handleResult(mockVaultPayload);

        verify(defaultBrainTreeLoggingHandler, times(8)).verifyIfEmpty(
                any(StringBuilder.class), anyString(),
                anyString(), anyString());
        verify(mockPaymentMethodDetails).get__typename();
        verify(mockPaymentMethodDetails).getLast4();
        verify(mockPaymentMethodDetails).getExpirationMonth();
        verify(mockPaymentMethodDetails).getExpirationYear();
    }

    @Test
    public void testHandleResultWithBrainTreeCustomer() {
        when(mockCustomer.getId()).thenReturn(MOCK_CUSTOMER_ID);
        when(mockCustomer.getFirstName()).thenReturn(FIRST_NAME);
        when(mockCustomer.getLastName()).thenReturn(LAST_NAME);
        when(mockCustomer.getEmail()).thenReturn(EMAIL);

        defaultBrainTreeLoggingHandler.handleResult(mockCustomer);

        verify(defaultBrainTreeLoggingHandler, times(4)).verifyIfEmpty(
                any(StringBuilder.class), anyString(),
                anyString(), anyString());
        verify(mockCustomer).getEmail();
        verify(mockCustomer).getFirstName();
        verify(mockCustomer).getLastName();
    }

    @Test
    public void testHandleResultWithPaymentMethodToken() {
        defaultBrainTreeLoggingHandler.handleResult(TOKEN);

        verify(defaultBrainTreeLoggingHandler, times(1)).verifyIfEmpty(
                any(StringBuilder.class), anyString(),
                anyString(), anyString());
    }

    @Test
    public void testHandleVerificationError() {
        BrainTreeVerification mockVerification = mock(BrainTreeVerification.class);
        BrainTreeVerificationProcessorResponse mockProcessorResponse = mock(BrainTreeVerificationProcessorResponse.class);

        when(mockVerification.getStatus()).thenReturn(CreditCardVerification.Status.GATEWAY_REJECTED.name());
        when(mockVerification.getGatewayRejectionReason()).thenReturn(REASON);

        when(mockVerification.getProcessorResponse()).thenReturn(mockProcessorResponse);
        when(mockProcessorResponse.getAvsPostalCodeResponse()).thenReturn(POSTAL_CODE_RESPONSE_CODE);
        when(mockProcessorResponse.getAvsStreetAddressResponse()).thenReturn(STREET_ADDRESS_RESPONSE_CODE);
        when(mockProcessorResponse.getLegacyCode()).thenReturn(PROCESSOR_RESPONSE_CODE);
        when(mockProcessorResponse.getMessage()).thenReturn(PROCESSOR_RESPONSE_TEXT);

        defaultBrainTreeLoggingHandler.handleVerificationError(mockVerification);

        verify(mockVerification, times(3)).getStatus();
        verify(mockVerification).getGatewayRejectionReason();
        verify(mockProcessorResponse).getAvsPostalCodeResponse();
        verify(mockProcessorResponse).getAvsStreetAddressResponse();
        verify(mockProcessorResponse).getLegacyCode();
        verify(mockProcessorResponse).getMessage();
    }

    @Test
    public void testHandleVerificationErrorWithProcessorDeclined() {
        BrainTreeVerification mockVerification = mock(BrainTreeVerification.class);
        BrainTreeVerificationProcessorResponse mockProcessorResponse = mock(BrainTreeVerificationProcessorResponse.class);

        when(mockVerification.getStatus()).thenReturn(CreditCardVerification.Status.PROCESSOR_DECLINED.name());
        when(mockVerification.getProcessorResponse()).thenReturn(mockProcessorResponse);
        when(mockProcessorResponse.getAvsPostalCodeResponse()).thenReturn(POSTAL_CODE_RESPONSE_CODE);
        when(mockProcessorResponse.getAvsStreetAddressResponse()).thenReturn(STREET_ADDRESS_RESPONSE_CODE);
        when(mockProcessorResponse.getLegacyCode()).thenReturn(PROCESSOR_RESPONSE_CODE);
        when(mockProcessorResponse.getMessage()).thenReturn(PROCESSOR_RESPONSE_TEXT);

        defaultBrainTreeLoggingHandler.handleVerificationError(mockVerification);

        verify(mockVerification, times(3)).getStatus();
        verify(mockVerification, never()).getGatewayRejectionReason();
        verify(mockProcessorResponse).getAvsPostalCodeResponse();
        verify(mockProcessorResponse).getAvsStreetAddressResponse();
        verify(mockProcessorResponse).getLegacyCode();
        verify(mockProcessorResponse).getMessage();
    }

    @Test
    public void formatSrcCardBeforeLogging() {
        when(mockVisaCardDetails.getLast4()).thenReturn(CARD_NUMBER_LAST_4);
        when(mockVisaCardDetails.getCardType()).thenReturn(CARD_TYPE);
        when(mockVisaCardDetails.getImageUrl()).thenReturn(IMAGE_URL);
        when(mockVisaCardDetails.getExpirationMonth()).thenReturn(EXPIRATION_MONTH);
        when(mockVisaCardDetails.getExpirationYear()).thenReturn(EXPIRATION_YEAR);
        when(mockVisaCardDetails.getToken()).thenReturn(TOKEN);

        String result = defaultBrainTreeLoggingHandler.formatSrcCard(mockVisaCardDetails);

        assertNotNull(result);
        assertEquals(EXPECTED_CARD_DATA, result);
    }

    @Test
    public void shouldNotFormatSrcCardBeforeLogging() {
        defaultBrainTreeLoggingHandler.formatSrcCard((VisaCheckoutCardDetails) null);

        verify(mockVisaCardDetails, never()).getToken();
        verify(mockVisaCardDetails, never()).getCardType();
        verify(mockVisaCardDetails, never()).getImageUrl();
    }

    @Test
    public void getLoggerShouldReturnLogger() {
        Logger result = defaultBrainTreeLoggingHandler.getLogger();

        assertNotNull(result);
        assertEquals(Logger.class, result.getClass());
    }
}
