package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeGenerateClientTokenRequest;
import com.braintree.command.result.BrainTreeGenerateClientTokenResult;
import com.braintree.commands.BrainTreeGenerateClientTokenCommand;
import com.braintree.commands.impl.AbstractCommand;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeClientTokenInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeCreateClientTokenInput;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeCreateClientTokenPayload;
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 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 BrainTreeGenerateClientTokenCommand and is used in GraphQL API.
 */
public class DefaultBrainTreeGraphQLGenerateClientTokenCommand extends AbstractCommand implements
        BrainTreeGenerateClientTokenCommand {

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

    private static final String DEFINITION_FILE_NAME = "createClientToken";
    private static final String MUTATION_NAME = "createClientToken";
    private static final String CLIENT_TOKEN_INPUT_LOG_MSG = "ClientTokenInput: merchantAccountId = %s, customerId = %s";
    private static final String ERROR_DUE_TO_CREATION_TOKEN_MESSAGE = "[Generate Token] Error due to creation token. ";

    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;

    @Override
    public BrainTreeGenerateClientTokenResult perform(
            BrainTreeGenerateClientTokenRequest brainTreeGenerateClientTokenRequest) {
        try {
            Map<String, Object> result = makeGraphQlCall(DEFINITION_FILE_NAME,
                    createVariablesMap(brainTreeGenerateClientTokenRequest));
            ArrayList<Map<String, Object>> mapErrors = (ArrayList<Map<String, Object>>) result.get(RESULT_ERRORS);

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

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

    private BrainTreeGenerateClientTokenResult translateResponse(Map<String, Object> data) {
        BrainTreeCreateClientTokenPayload payload = objectMapper
                .convertValue(data.get(MUTATION_NAME), BrainTreeCreateClientTokenPayload.class);

        return new BrainTreeGenerateClientTokenResult(payload.getClientToken());
    }

    private Map<String, Object> createVariablesMap(final BrainTreeGenerateClientTokenRequest request) {

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

        final BrainTreeCreateClientTokenInput input = new BrainTreeCreateClientTokenInput();
        final BrainTreeClientTokenInput clientTokenInput = new BrainTreeClientTokenInput();

        if (StringUtils.isNotEmpty(request.getCustomerId())) {
            clientTokenInput
                    .setCustomerId(brainTreeCustomerAccountService.getGraphQLIdForCustomer(request.getCustomerId()));
        }
        if (request.getMerchantAccount() != null) {
            clientTokenInput.setMerchantAccountId(request.getMerchantAccount());
        }

        input.setClientToken(clientTokenInput);
        map.put(INPUT_PARAMETER, input);
        logClientTokenInput(clientTokenInput);

        return map;
    }

    private void logClientTokenInput(BrainTreeClientTokenInput clientTokenInput) {
        LOG.info(String.format(CLIENT_TOKEN_INPUT_LOG_MSG, clientTokenInput.getMerchantAccountId(),
                clientTokenInput.getCustomerId()));
    }

    public BrainTreeCustomerAccountService getBrainTreeCustomerAccountService() {
        return brainTreeCustomerAccountService;
    }

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

}
