package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeCreatePaymentMethodRequest;
import com.braintree.command.result.BrainTreeCreatePaymentMethodResult;
import com.braintree.commands.BrainTreeCreateUsBankAccountPaymentMethodCommand;
import com.braintree.commands.impl.AbstractCommand;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeVaultUsBankAccountInput;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodOrigin;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeVaultPaymentMethodPayload;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import de.hybris.platform.payment.AdapterException;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static com.braintree.constants.BraintreeConstants.INPUT_PARAMETER;
import static com.braintree.constants.BraintreeConstants.RESULT_DATA;
import static com.braintree.constants.BraintreeConstants.RESULT_ERRORS;

public class DefaultBrainTreeGraphQLCreateUsBankAccountPaymentMethodCommand extends AbstractCommand implements
        BrainTreeCreateUsBankAccountPaymentMethodCommand {

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

    private static final String VAULT_US_BANK_ACCOUNT = "vaultUsBankAccount";
    private static final String ERROR_DUE_TO_CREATION_OF_PAYMENT_METHOD_MESSAGE =
            "[Create payment method] Error due to creation of payment method. ";

    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;


    private BraintreePaymentInfoService braintreePaymentInfoService;

    @Override
    public BrainTreeCreatePaymentMethodResult perform(BrainTreeCreatePaymentMethodRequest brainTreeCreatePaymentMethodRequest) {
        try {
            Map<String, Object> result = makeGraphQlCall(VAULT_US_BANK_ACCOUNT,
                    createVariablesMap(brainTreeCreatePaymentMethodRequest));
            ArrayList<Map<String, Object>> mapErrors = (ArrayList<Map<String, Object>>) result.get(RESULT_ERRORS);

            if (mapErrors == null) {
                return translateResult((Map<String, Object>) result.get(RESULT_DATA));
            } else {
                final String errorMessage =
                        ERROR_DUE_TO_CREATION_OF_PAYMENT_METHOD_MESSAGE + getFistErrorMessage(
                                mapErrors);
                LOG.error(errorMessage);
                throw new IllegalArgumentException();
            }

        } catch (final Exception exception) {
            throw new AdapterException(exception.getMessage(), exception);
        }
    }

    private Map<String, Object> createVariablesMap(final BrainTreeCreatePaymentMethodRequest request) {
        getLoggingHandler().handleCreatePaymentMethodRequest(request);
        Map<String, Object> map = new HashMap<>();

        BrainTreeVaultUsBankAccountInput input = new BrainTreeVaultUsBankAccountInput();
        input.setPaymentMethodId(request.getMethodNonce());
        input.setVerificationMethod(request.getOptions().getUsBankAccountVerificationMethod());
        if (StringUtils.isNotEmpty(request.getCustomerId())) {
            input.setCustomerId(brainTreeCustomerAccountService.getGraphQLIdForCustomer(request.getCustomerId()));
        }
        input.setVerificationMerchantAccountId(getBrainTreeConfigService().getMerchantAccountIdForCurrentSiteAndCurrency());
        map.put(INPUT_PARAMETER, input);
        return map;
    }

    private BrainTreeCreatePaymentMethodResult translateResult(Map<String, Object> data) {

        final BrainTreeCreatePaymentMethodResult result = new BrainTreeCreatePaymentMethodResult();

        BrainTreeVaultPaymentMethodPayload payload = objectMapper
                .convertValue(data.get(VAULT_US_BANK_ACCOUNT), BrainTreeVaultPaymentMethodPayload.class);

        BraintreePaymentMethodData paymentMethod = payload.getPaymentMethod();

        result.setSuccess(true);
        result.setVerified(paymentMethod.getDetails().isVerified());
        result.setPaymentMethodToken(paymentMethod.getLegacyId());
        result.setPaymentMethodGraphQLToken(paymentMethod.getId());
        result.setLast4(paymentMethod.getDetails().getLast4());
        result.setRoutingNumber(paymentMethod.getDetails().getRoutingNumber());
        String originType = Optional.ofNullable(paymentMethod.getDetails().getOrigin()).map(BrainTreePaymentMethodOrigin::getType)
                .orElse(null);
        String paymentProvider = braintreePaymentInfoService
                .getPaymentProviderByDetailsAndOrigin(paymentMethod.getDetails().get__typename(), originType);
        result.setPaymentProvider(paymentProvider);
        result.setImageSource(braintreePaymentInfoService
                .getImageSourceByPaymentProviderAndCardType(paymentProvider, paymentMethod.getDetails().getBrandCode()));

        getLoggingHandler().handleResult(payload);
        return result;
    }

    public void setBrainTreeCustomerAccountService(
            BrainTreeCustomerAccountService brainTreeCustomerAccountService) {
        this.brainTreeCustomerAccountService = brainTreeCustomerAccountService;
    }

    public void setBraintreePaymentInfoService(
            BraintreePaymentInfoService braintreePaymentInfoService) {
        this.braintreePaymentInfoService = braintreePaymentInfoService;
    }

}
