package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeTokenizeCreditCardRequest;
import com.braintree.command.result.BrainTreeTokenizeCardResult;
import com.braintree.commands.impl.AbstractCommand;
import com.braintree.graphql.commands.BrainTreeGraphQLTokenizeCreditCardCommand;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeCreditCardInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeTokenizeCreditCardInput;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTokenizeCreditCardPayload;
import de.hybris.platform.payment.AdapterException;
import org.apache.log4j.Logger;

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

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

/**
 * This class extends AbstractCommand, implements BrainTreeGraphQLTokenizeCreditCardCommand and is used in GraphQL API.
 */
public class DefaultBrainTreeGraphQLTokenizeCreditCardCommand extends AbstractCommand implements
        BrainTreeGraphQLTokenizeCreditCardCommand {

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

    private static final String DEFINITION_FILE_NAME_TOKENIZE_CARD = "tokenizeCreditCard";
    private static final String CREDIT_CARD_ERROR_DUE_TOKENIZATION_CREDIT_MESSAGE = "[BT tokenize credit card] Error due tokenization credit. ";
    private static final String TOKENIZE_CREDIT_CARD_REQUEST_ERROR_MESSAGE = "Error during tokenize credit card. Request: ";

    @Override
    public BrainTreeTokenizeCardResult perform(BrainTreeTokenizeCreditCardRequest request) {

        try {
            Map<String, Object> result = makeGraphQlCall(DEFINITION_FILE_NAME_TOKENIZE_CARD,
                    createVariablesMap(request));
            ArrayList<Map<String, Object>> mapErrors = (ArrayList<Map<String, Object>>) result.get(RESULT_ERRORS);

            BrainTreeTokenizeCardResult tokenizeCardResult;
            if (mapErrors == null) {
                tokenizeCardResult = translateResponse((Map<String, Object>) result.get(RESULT_DATA));
            } else {
                String errorMessage =
                        CREDIT_CARD_ERROR_DUE_TOKENIZATION_CREDIT_MESSAGE + getFistErrorMessage(mapErrors);
                LOG.error(errorMessage);
                throw new IllegalArgumentException(errorMessage);
            }
            return tokenizeCardResult;
        } catch (final Exception exception) {
            LOG.error(TOKENIZE_CREDIT_CARD_REQUEST_ERROR_MESSAGE + request, exception);
            throw new AdapterException(exception.getMessage(), exception);
        }
    }

    private BrainTreeTokenizeCardResult translateResponse(Map<String, Object> data) {
        BrainTreeTokenizeCardResult tokenizeCardResult = new BrainTreeTokenizeCardResult();
        BrainTreeTokenizeCreditCardPayload payload = objectMapper
                .convertValue(data.get(DEFINITION_FILE_NAME_TOKENIZE_CARD), BrainTreeTokenizeCreditCardPayload.class);

        BraintreePaymentMethodData paymentMethod = payload.getPaymentMethod();
        if (paymentMethod != null) {
            tokenizeCardResult.setMethodId(paymentMethod.getLegacyId());
            tokenizeCardResult.setMethodGraphQLId(paymentMethod.getId());
        }

        return tokenizeCardResult;
    }

    private Map<String, Object> createVariablesMap(BrainTreeTokenizeCreditCardRequest request) {
        Map<String, Object> map = new HashMap<>();

        BrainTreeTokenizeCreditCardInput input = new BrainTreeTokenizeCreditCardInput();
        BrainTreeCreditCardInput creditCard = new BrainTreeCreditCardInput();
        input.setCreditCard(creditCard);

        creditCard.setCardholderName(request.getCardHolderName());
        creditCard.setNumber(request.getCardNumber());
        creditCard.setCvv(request.getCvv());
        creditCard.setExpirationMonth(request.getExpirationMonth());
        creditCard.setExpirationYear(request.getExpirationYear());

        map.put(INPUT_PARAMETER, input);

        return map;
    }

}
