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.constants.BraintreeConstants;
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.BrainTreePaymentMethodDetails;
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.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.VenmoAccount;
import com.braintreegateway.VisaCheckoutCard;
import com.braintreegateway.VisaCheckoutCardDetails;
import de.hybris.platform.payment.commands.request.CreateSubscriptionRequest;
import de.hybris.platform.payment.dto.BillingInfo;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.text.SimpleDateFormat;
import java.util.List;

import static de.hybris.platform.servicelayer.util.ServicesUtil.validateParameterNotNullStandardMessage;
import static org.apache.commons.lang.StringUtils.EMPTY;


public class DefaultBrainTreeLoggingHandler {

    private static final String NEW_LINE = System.getProperty("line.separator");
    private static final String AUTHORIZATION_REQUEST = "[AUTHORIZATION REQUEST]";
    private static final String CREATE_SUBSCRIPTION_REQUEST = "[CREATE CUSTOMER REQUEST]";
    private static final String FIRST_NAME = "First name";
    private static final String LAST_NAME = "Last name";
    private static final String CUSTOMER_ID = "Customer ID";
    private static final String CREATED = "CREATED";
    private static final String IMAGE_URL = "Image url";
    private static final String TOKEN = "Token";
    private static final String GRAPHQL_TOKEN = "GraphQL token";
    private static final String EMAIL = "Email";
    private static final String AMOUNT = "Amount";
    private static final String CURRENCY_ISO_CODE = "Currency iso code";
    private static final String STATUS = "Status";
    private static final String CARDHOLDER_NAME = "Cardholder name";
    private static final String BRAND_CODE = "Brand code";
    private static final String LAST4 = "Last four digits";
    private static final String EXPIRATION_MONTH = "Expiration month";
    private static final String EXPIRATION_YEAR = "Expiration year";
    private static final String ORIGIN = "Origin";
    private static final String CARD_TYPE = "Card type";
    private static final String CARD_NUMBER = "Card number";
    private static final String EXPIRATION = "Expiration";
    private static final String BRAIN_TREE_CARD_VALIDATION_ERROR_STATUS_MESSAGE  = "[BrainTree Card Validation Error] [status = ";

    private static final Logger LOG = Logger.getLogger(DefaultBrainTreeLoggingHandler.class);

    public Logger getLogger() {
        return LOG;
    }

    private static StringBuilder getSB() {
        return new StringBuilder();
    }

    public void handleAuthorizationRequest(final BrainTreeAuthorizationRequest authorizationRequest) {
        validateParameterNotNullStandardMessage("captureRequest", authorizationRequest);
        validateParameterNotNullStandardMessage("totalAmount", authorizationRequest.getTotalAmount());

        final StringBuilder sb = getSB();

        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, CUSTOMER_ID, authorizationRequest.getCustomerId());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Total amount", authorizationRequest.getTotalAmount().toString());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Payment type", authorizationRequest.getPaymentType());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Device data", authorizationRequest.getDeviceData());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Method nonce", authorizationRequest.getMethodNonce());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Payment type", authorizationRequest.getPaymentType());
        if (authorizationRequest.getLiabilityShifted() != null) {
            verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Liability shifted",
                authorizationRequest.getLiabilityShifted().toString());
        }
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Payment method token", authorizationRequest.getPaymentMethodToken());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "ThreeD secure config",
            authorizationRequest.getThreeDSecureConfiguration()
                .toString());
        if (authorizationRequest.getAdvancedFraudTools() != null) {
            verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Advanced fraud tools",
                authorizationRequest.getAdvancedFraudTools().toString());
        }

        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "BrainTree channel", authorizationRequest.getBrainTreeChannel());
        verifyIfEmpty(sb, AUTHORIZATION_REQUEST, "Store in vault",
            String.valueOf(authorizationRequest.isStoreInVault()));

        LOG.info(sb.toString());
    }

    public void handleAddressRequest(final String type, final BrainTreeAddressRequest addressRequest) {
        final StringBuilder sb = getSB();
        verifyIfEmpty(sb, type, "Address Id", addressRequest.getAddressId());
        verifyIfEmpty(sb, type, "Company", addressRequest.getCompany());
        verifyIfEmpty(sb, type, "Country", addressRequest.getCountryCodeAlpha2());
        verifyIfEmpty(sb, type, CUSTOMER_ID, addressRequest.getCustomerId());
        verifyIfEmpty(sb, type, "Extended address", addressRequest.getExtendedAddress());
        verifyIfEmpty(sb, type, FIRST_NAME, addressRequest.getFirstName());
        verifyIfEmpty(sb, type, LAST_NAME, addressRequest.getLastName());
        verifyIfEmpty(sb, type, "City", addressRequest.getLocality());
        verifyIfEmpty(sb, type, "Postal code", addressRequest.getPostalCode());
        verifyIfEmpty(sb, type, "Region", addressRequest.getRegion());
        verifyIfEmpty(sb, type, "Street address", addressRequest.getStreetAddress());

        LOG.info(sb.toString());
    }

    public void handleResult(final String type, final Address address) {

        final StringBuilder sb = getSB();

        sb.append(formatAddress(type, address));

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final Transaction transaction) {

        final StringBuilder sb = getSB();

        sb.append(formatTransaction(type, transaction));

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final BrainTreeTransactionPayload payload) {

        final StringBuilder sb = getSB();

        sb.append(formatTransaction(type, payload));

        LOG.info(sb.toString());

    }


    public void handleResult(final String type, final BrainTreeTransaction reversal) {

        final StringBuilder sb = getSB();

        sb.append(formatTransaction(type, reversal));

        LOG.info(sb.toString());

    }

    public void handleCreatePaymentMethodRequest(final BrainTreeCreatePaymentMethodRequest request) {
        final StringBuilder sb = getSB();
        verifyIfEmpty(sb, "[PAYMENT METHOD REQUEST]", CUSTOMER_ID, request.getCustomerId());
        verifyIfEmpty(sb, "[PAYMENT METHOD REQUEST]", "Method nonce", request.getMethodNonce());

        LOG.info(sb.toString());
    }

    public void handleResult(final String type, final CreditCard creditCard) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        formatCreditCard(type, creditCard);

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final PayPalAccount payPalAccount) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        formatPayPalAccount(type, payPalAccount);

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final VenmoAccount venmoAccount) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        formatVenmo(type, venmoAccount);

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final AndroidPayCard googlePay) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        sb.append(formatAndroidPayCard(googlePay));

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final VisaCheckoutCard src) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        sb.append(formatSrcCard(src));

        LOG.info(sb.toString());

    }

    public void handleResult(final String type, final UsBankAccount usBankAccount) {
        final StringBuilder sb = getSB();
        sb.append(type).append(CREATED).append(NEW_LINE);
        sb.append(formatUsBankAccountCard(type, usBankAccount));

        LOG.info(sb.toString());
    }

    private String formatPayPalAccount(String type, final PayPalAccount payPalAccount) {
        if (payPalAccount == null) {
            return EMPTY;
        }
        final StringBuilder sb = getSB();
        type = type + "[CREDIT CARD]";

        verifyIfEmpty(sb, type, CUSTOMER_ID, payPalAccount.getCustomerId());
        verifyIfEmpty(sb, type, IMAGE_URL, payPalAccount.getImageUrl());
        verifyIfEmpty(sb, type, TOKEN, payPalAccount.getToken());
        verifyIfEmpty(sb, type, "PayPal email", payPalAccount.getEmail());

        return sb.toString();
    }

    private String formatVenmo(String type, final VenmoAccount venmoAccount) {
        if (venmoAccount == null) {
            return EMPTY;
        }
        final StringBuilder sb = getSB();
        type = type + "[VENMO]";

        verifyIfEmpty(sb, type, CUSTOMER_ID, venmoAccount.getCustomerId());
        verifyIfEmpty(sb, type, IMAGE_URL, venmoAccount.getImageUrl());
        verifyIfEmpty(sb, type, TOKEN, venmoAccount.getToken());

        return sb.toString();
    }

    private String formatUsBankAccountCard(String type, final UsBankAccount venmoAccount) {
        if (venmoAccount == null) {
            return EMPTY;
        }
        final StringBuilder sb = getSB();
        type = type + "[VENMO]";

        verifyIfEmpty(sb, type, CUSTOMER_ID, venmoAccount.getCustomerId());
        verifyIfEmpty(sb, type, IMAGE_URL, venmoAccount.getImageUrl());
        verifyIfEmpty(sb, type, TOKEN, venmoAccount.getToken());

        return sb.toString();
    }

    public String handleValidationErrors(final List<ValidationError> errors) {
        if (errors != null && !errors.isEmpty()) {
            final StringBuilder errorLog = new StringBuilder();
            for (final ValidationError error : errors) {
                formatError(errorLog, error);
            }
            LOG.error(errorLog.toString());

            //return first error message for error stack trace
            return formatError(new StringBuilder(), errors.get(0));
        }
        return StringUtils.EMPTY;
    }

    public String handleErrors(final List<BrainTreeErrorDefinition> errors) {
        if (errors != null && !errors.isEmpty()) {
            final StringBuilder errorLog = new StringBuilder();
            for (final BrainTreeErrorDefinition error : errors) {
                formatError(errorLog, error);
            }
            LOG.error(errorLog.toString());

            //return first error message for error stack trace
            return formatError(new StringBuilder(), errors.get(0));
        }
        return StringUtils.EMPTY;
    }

    public void handleCardVerificationError(CreditCardVerification creditCardVerification) {
        if (creditCardVerification != null) {
            final StringBuilder errorLog = new StringBuilder();
            errorLog.append("[BrainTree Card Validation Details] [Postal Code response code= ")
                .append(creditCardVerification.getAvsPostalCodeResponseCode()).append("]")
                .append("[Street address response code= ")
                .append(creditCardVerification.getAvsStreetAddressResponseCode())
                .append("]").append(NEW_LINE);
            if (creditCardVerification.getStatus().equals(CreditCardVerification.Status.GATEWAY_REJECTED)) {
                creditCardVerification.getGatewayRejectionReason();
                errorLog.append(BRAIN_TREE_CARD_VALIDATION_ERROR_STATUS_MESSAGE)
                    .append(creditCardVerification.getStatus())
                    .append(" reason = ").append(creditCardVerification.getGatewayRejectionReason())
                    .append("]")
                    .append(NEW_LINE);
            }
            if (creditCardVerification.getStatus().equals(CreditCardVerification.Status.PROCESSOR_DECLINED)) {
                errorLog.append(BRAIN_TREE_CARD_VALIDATION_ERROR_STATUS_MESSAGE)
                    .append(creditCardVerification.getStatus())
                    .append(NEW_LINE);
            }

            errorLog.append("[BrainTree Card Validation Error] [code = ")
                .append(creditCardVerification.getProcessorResponseCode())
                .append(" message = ").append(creditCardVerification.getProcessorResponseText()).append("]")
                .append(NEW_LINE);
            LOG.error(errorLog.toString());
        }
    }

    protected String formatError(final StringBuilder errorLog, final ValidationError error) {
        errorLog.append("[BrainTree Validation Error] [code = ").append(error.getCode()).append("(")
            .append(error.getCode().code)
            .append(") message = ").append(error.getMessage()).append("]").append(NEW_LINE);
        return errorLog.toString();
    }

    protected String formatError(final StringBuilder errorLog, final BrainTreeErrorDefinition error) {
        errorLog.append("[BrainTree Validation Error] [code = (").append(error.getExtensions().getLegacyCode())
            .append(") message = ").append(error.getMessage()).append("]").append(NEW_LINE);
        return errorLog.toString();
    }

    public void handleResult(final String type, final Customer customer) {
        final StringBuilder sb = getSB();
        formatCustomer(type, customer);
        LOG.info(sb.toString());
    }

    public void handleCreateSubscriptionRequest(final CreateSubscriptionRequest request, final String payee) {
        validateParameterNotNullStandardMessage("Create User Request", request);
        final BillingInfo billingInfo = request.getBillingInfo();
        validateParameterNotNullStandardMessage("Billing Info", billingInfo);

        final StringBuilder sb = getSB();

        verifyIfEmpty(sb, CREATE_SUBSCRIPTION_REQUEST, "Customer first name", billingInfo.getFirstName());
        verifyIfEmpty(sb, CREATE_SUBSCRIPTION_REQUEST, "Customer last name", billingInfo.getLastName());
        verifyIfEmpty(sb, CREATE_SUBSCRIPTION_REQUEST, "Customer email", billingInfo.getEmail());
        verifyIfEmpty(sb, CREATE_SUBSCRIPTION_REQUEST, "Customer payee email", payee);

        LOG.info(sb.toString());

    }


    private String formatCustomer(String type, final Customer customer) {
        if (customer == null) {
            return EMPTY;
        }

        final StringBuilder sb = getSB();
        type = "[CUSTOMER]" + type;

        verifyIfEmpty(sb, type, "ID", customer.getId());
        verifyIfEmpty(sb, type, FIRST_NAME, customer.getFirstName());
        verifyIfEmpty(sb, type, LAST_NAME, customer.getLastName());
        verifyIfEmpty(sb, type, EMAIL, customer.getEmail());

        return sb.toString();
    }


    protected String formatTransaction(final String type, final Transaction transaction) {

        final SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

        if (transaction == null) {
            return EMPTY;
        }

        final StringBuilder sb = new StringBuilder();
        verifyIfEmpty(sb, type, "Merchant account ID", formatMerchantAccountId(transaction.getMerchantAccountId()));
        verifyIfEmpty(sb, type, "Id", transaction.getId());
        verifyIfEmpty(sb, type, AMOUNT, transaction.getAmount().toString());
        verifyIfEmpty(sb, type, "Order ID", transaction.getOrderId());
        verifyIfEmpty(sb, type, CURRENCY_ISO_CODE, transaction.getCurrencyIsoCode());
        verifyIfEmpty(sb, type, "Date", df.format(transaction.getUpdatedAt().getTime()));
        verifyIfEmpty(sb, type, STATUS, transaction.getStatus().toString());

        sb.append(formatCustomer(StringUtils.EMPTY, transaction.getCustomer()));

        //TODO NEED REFACTORING FOR OTHERS PAYMENT PROVIDERS
        if (transaction.getAndroidPayDetails() != null) {
            sb.append(formatAndroidPayCard(transaction.getAndroidPayDetails()));
        } else if (transaction.getVisaCheckoutCardDetails() != null) {
            sb.append(formatSrcCard(transaction.getVisaCheckoutCardDetails()));
        } else {
            sb.append(formatCreditCard(StringUtils.EMPTY, transaction.getCreditCard()));
        }

        sb.append(formatAddress("[BILLING ADDRESS]", transaction.getBillingAddress()));
        sb.append(formatAddress("[SHIPPING ADDRESS]", transaction.getShippingAddress()));

        return sb.toString();
    }

    protected String formatTransaction(final String type, final BrainTreeTransactionPayload payload) {

        final SimpleDateFormat df = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");

        if (payload == null) {
            return EMPTY;
        }
        final BrainTreeTransaction transaction = payload.getTransaction();

        final StringBuilder sb = new StringBuilder();
        verifyIfEmpty(sb, type, "Merchant account ID", formatMerchantAccountId(transaction.getMerchantAccountId()));
        verifyIfEmpty(sb, type, "Id", transaction.getLegacyId());
        verifyIfEmpty(sb, type, "GraphQLId", transaction.getId());
        verifyIfEmpty(sb, type, AMOUNT, transaction.getAmount().toString());
        verifyIfEmpty(sb, type, CURRENCY_ISO_CODE, transaction.getAmount().getCurrencyCode());
        verifyIfEmpty(sb, type, "Date", df.format(transaction.getCreatedAt().getTime()));
        if(transaction.getStatus() != null) {
            verifyIfEmpty(sb, type, STATUS, transaction.getStatus().name());
        }

        if (payload.getTransaction().getPaymentMethod() != null) {
            sb.append(formatPaymentDetails(payload.getTransaction().getPaymentMethod()));
        }

        return sb.toString();
    }

    private String formatPaymentDetails(BraintreePaymentMethodData paymentMethod) {
        final StringBuilder sb = getSB();

        final BrainTreePaymentMethodDetails details = paymentMethod.getDetails();
        final String type = details.get__typename();

        if (details.getOrigin() != null && details.getOrigin().getType() != null) {
            verifyIfEmpty(sb, type, ORIGIN, details.getOrigin().getType());
        }
        if (details.getLast4() != null) {
            verifyIfEmpty(sb, type, CARD_NUMBER,
                String.format(BraintreeConstants.CARD_NUMBER_MASK, details.getLast4()));
        }
        if (details.getBrandCode() != null) {
            verifyIfEmpty(sb, type, CARD_TYPE, details.getBrandCode());
        }
        if (details.getExpirationMonth() != null && details.getExpirationYear() != null) {
            verifyIfEmpty(sb, type, EXPIRATION,
                details.getExpirationMonth() + "-" + details.getExpirationYear());
        }
        if (paymentMethod.getLegacyId() != null) {
            verifyIfEmpty(sb, type, TOKEN, paymentMethod.getLegacyId());
        }
        if (paymentMethod.getId() != null) {
            verifyIfEmpty(sb, type, GRAPHQL_TOKEN, paymentMethod.getId());
        }

        return sb.toString();
    }

    protected String formatTransaction(final String type, final BrainTreeTransaction reversal) {

        if (reversal == null) {
            return EMPTY;
        }

        final StringBuilder sb = new StringBuilder();
        verifyIfEmpty(sb, type, "GraphQlId", reversal.getId());
        verifyIfEmpty(sb, type, "LegacyId", reversal.getLegacyId());
        verifyIfEmpty(sb, type, AMOUNT, reversal.getAmount().getValue());
        verifyIfEmpty(sb, type, CURRENCY_ISO_CODE, reversal.getAmount().getCurrencyCode());
        if(reversal.getStatus() != null) {
            verifyIfEmpty(sb, type, STATUS, reversal.getStatus().name());
        }

        return sb.toString();
    }

    private static String formatMerchantAccountId(final String merchantAccountId) {
        final int len = merchantAccountId.length();
        return merchantAccountId.substring(0, 2) + "****" + merchantAccountId.substring(len - 2, len);
    }

    private String formatCreditCard(String type, final CreditCard card) {
        final StringBuilder sb = getSB();

        if (card == null) {
            return EMPTY;
        }

        type = type + "[CREDIT CARD]";

        verifyIfEmpty(sb, type, "Card holder full name", card.getCardholderName());
        verifyIfEmpty(sb, type, CUSTOMER_ID, card.getCustomerId());
        verifyIfEmpty(sb, type, CARD_TYPE, card.getCardType());
        verifyIfEmpty(sb, type, CARD_NUMBER, card.getMaskedNumber());
        verifyIfEmpty(sb, type, IMAGE_URL, card.getImageUrl());
        verifyIfEmpty(sb, type, EXPIRATION, card.getExpirationMonth() + "-" + card.getExpirationYear());
        verifyIfEmpty(sb, type, TOKEN, card.getToken());
        verifyIfEmpty(sb, type, "Bank issue", card.getIssuingBank());

        sb.append(formatAddress("[BILLING ADDRESS]", card.getBillingAddress()));
        return sb.toString();

    }

    private String formatAndroidPayCard(final AndroidPayDetails androidPayDetails) {
        final StringBuilder sb = getSB();
        final String type;

        type = "[GOOGLE PAY CARD]";

        verifyIfEmpty(sb, type, CARD_TYPE, androidPayDetails.getSourceCardType());
        verifyIfEmpty(sb, type, CARD_NUMBER,
            String.format(BraintreeConstants.CARD_NUMBER_MASK, androidPayDetails.getSourceCardLast4()));
        verifyIfEmpty(sb, type, IMAGE_URL, androidPayDetails.getImageUrl());
        verifyIfEmpty(sb, type, EXPIRATION,
            androidPayDetails.getExpirationMonth() + "-" + androidPayDetails.getExpirationYear());
        verifyIfEmpty(sb, type, TOKEN, androidPayDetails.getToken());

        return sb.toString();
    }

    private String formatAndroidPayCard(final AndroidPayCard androidPayCard) {
        final StringBuilder sb = getSB();
        final String type;

        if (androidPayCard == null) {
            return EMPTY;
        }

        type = "[GOOGLE PAY CARD]";

        verifyIfEmpty(sb, type, CUSTOMER_ID, androidPayCard.getCustomerId());
        verifyIfEmpty(sb, type, CARD_TYPE, androidPayCard.getSourceCardType());
        verifyIfEmpty(sb, type, CARD_NUMBER,
            String.format(BraintreeConstants.CARD_NUMBER_MASK, androidPayCard.getSourceCardLast4()));
        verifyIfEmpty(sb, type, IMAGE_URL, androidPayCard.getImageUrl());
        verifyIfEmpty(sb, type, EXPIRATION,
            androidPayCard.getExpirationMonth() + "-" + androidPayCard.getExpirationYear());
        verifyIfEmpty(sb, type, TOKEN, androidPayCard.getToken());

        return sb.toString();
    }

    protected String formatSrcCard(final VisaCheckoutCardDetails src) {
        final StringBuilder sb = getSB();
        final String type;

        if (src == null) {
            return EMPTY;
        }

        type = "[SRC CARD]";

        verifyIfEmpty(sb, type, CARD_NUMBER,
            String.format(BraintreeConstants.CARD_NUMBER_MASK, src.getLast4()));
        verifyIfEmpty(sb, type, CARD_TYPE, src.getCardType());
        verifyIfEmpty(sb, type, IMAGE_URL, src.getImageUrl());
        verifyIfEmpty(sb, type, EXPIRATION,
            src.getExpirationMonth() + "-" + src.getExpirationYear());
        verifyIfEmpty(sb, type, TOKEN, src.getToken());

        return sb.toString();
    }

    protected String formatSrcCard(final VisaCheckoutCard src) {
        final StringBuilder sb = getSB();
        final String type;

        if (src == null) {
            return EMPTY;
        }

        type = "[SRC CARD]";

        verifyIfEmpty(sb, type, CUSTOMER_ID, src.getCustomerId());
        verifyIfEmpty(sb, type, CARD_TYPE, src.getCardType());
        verifyIfEmpty(sb, type, CARD_NUMBER,
            String.format(BraintreeConstants.CARD_NUMBER_MASK, src.getLast4()));
        verifyIfEmpty(sb, type, IMAGE_URL, src.getImageUrl());
        verifyIfEmpty(sb, type, EXPIRATION,
            src.getExpirationMonth() + "-" + src.getExpirationYear());
        verifyIfEmpty(sb, type, TOKEN, src.getToken());

        return sb.toString();
    }

    private String formatAddress(final String type, final Address address) {
        final StringBuilder sb = getSB();
        if (address == null) {
            return EMPTY;
        }

        verifyIfEmpty(sb, type, FIRST_NAME, address.getFirstName());
        verifyIfEmpty(sb, type, LAST_NAME, address.getLastName());
        verifyIfEmpty(sb, type, "Street address", address.getStreetAddress());
        verifyIfEmpty(sb, type, "Extended address", address.getExtendedAddress());
        verifyIfEmpty(sb, type, "Locality", address.getLocality());
        verifyIfEmpty(sb, type, "Region", address.getRegion());
        verifyIfEmpty(sb, type, "Postal code", address.getPostalCode());
        verifyIfEmpty(sb, type, "Country name", address.getCountryName());

        return sb.toString();
    }

    public void handleResult(BrainTreeVaultPaymentMethodPayload payload) {

        final StringBuilder sb = new StringBuilder();
        final BrainTreePaymentMethodDetails details = payload.getPaymentMethod().getDetails();
        final String type = "[" + details.get__typename() + "]";

        sb.append(type).append(" ").append(CREATED).append(NEW_LINE);
        verifyIfEmpty(sb, type, GRAPHQL_TOKEN, payload.getPaymentMethod().getId());
        verifyIfEmpty(sb, type, TOKEN, payload.getPaymentMethod().getLegacyId());
        verifyIfEmpty(sb, type, EMAIL, details.getEmail());
        verifyIfEmpty(sb, type, BRAND_CODE, details.getBrandCode());
        verifyIfEmpty(sb, type, CARDHOLDER_NAME, details.getCardholderName());
        verifyIfEmpty(sb, type, EXPIRATION_MONTH, details.getExpirationMonth());
        verifyIfEmpty(sb, type, EXPIRATION_YEAR, details.getExpirationYear());
        verifyIfEmpty(sb, type, LAST4, details.getLast4());

        LOG.info(sb.toString());

    }

    public void handleResult(BrainTreeCustomer customer) {

        final StringBuilder sb = new StringBuilder();

        final String type = "[CREATE CUSTOMER]";

        verifyIfEmpty(sb, type, "ID", customer.getId());
        verifyIfEmpty(sb, type, "FIRST_NAME", customer.getFirstName());
        verifyIfEmpty(sb, type, "LAST_NAME", customer.getLastName());
        verifyIfEmpty(sb, type, "EMAIL", customer.getEmail());

        LOG.info(sb.toString());

    }

    public void handleResult(String paymentMethodToken) {

        final StringBuilder sb = new StringBuilder();
        final String type = "[UPDATE BILLING ADDRESS]";

        verifyIfEmpty(sb, type, "PAYMENT_METHOD_TOKEN", paymentMethodToken);

        LOG.info(sb.toString());
    }

    public void verifyIfEmpty(final StringBuilder sb, final String type, final String name, final String value) {
        if (StringUtils.isNotEmpty(value)) {
            sb.append(type).append(name).append(" = ").append(value).append(NEW_LINE);
        } else {
            sb.append(type).append(name).append(" = ").append(StringUtils.EMPTY).append(NEW_LINE);
        }
    }

    public void handleVerificationError(BrainTreeVerification verification) {
        if (verification != null) {
            final StringBuilder errorLog = new StringBuilder();
            errorLog.append("[BrainTree Card Validation Details] [Postal Code response code= ")
                .append(verification.getProcessorResponse().getAvsPostalCodeResponse()).append("]")
                .append("[Street address response code= ")
                .append(verification.getProcessorResponse().getAvsStreetAddressResponse())
                .append("]").append(NEW_LINE);
            if (verification.getStatus().equals(CreditCardVerification.Status.GATEWAY_REJECTED.name())) {
                errorLog.append(BRAIN_TREE_CARD_VALIDATION_ERROR_STATUS_MESSAGE)
                    .append(verification.getStatus())
                    .append(" reason = ").append(verification.getGatewayRejectionReason())
                    .append("]")
                    .append(NEW_LINE);
            }
            if (verification.getStatus().equals(CreditCardVerification.Status.PROCESSOR_DECLINED.name())) {
                errorLog.append(BRAIN_TREE_CARD_VALIDATION_ERROR_STATUS_MESSAGE)
                    .append(verification.getStatus())
                    .append(NEW_LINE);
            }

            errorLog.append("[BrainTree Card Validation Error] [code = ")
                .append(verification.getProcessorResponse().getLegacyCode())
                .append(" message = ").append(verification.getProcessorResponse().getMessage()).append("]")
                .append(NEW_LINE);
            LOG.error(errorLog.toString());
        }
    }
}
