package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeUpdateCustomerRequest;
import com.braintree.command.result.BrainTreeUpdateCustomerResult;
import com.braintree.commands.BrainTreeUpdateCustomerCommand;
import com.braintree.commands.impl.AbstractCommand;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeCustomerInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeUpdateCustomerInput;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeCustomer;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeUpdateCustomerPayload;
import com.braintreegateway.ValidationError;
import de.hybris.platform.payment.AdapterException;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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 DefaultBrainTreeGraphQLUpdateCustomerCommand extends AbstractCommand implements
        BrainTreeUpdateCustomerCommand {

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

    private static final String DEFINITION_FILE_NAME = "updateCustomer";
    private static final String MUTATION_NAME = "updateCustomer";
    private static final String ERROR_DURING_CUSTOMER_UPDATE_MESSAGE = "[BT Update Customer] Error during customer update!";
    private static final String CUSTOMER_ID_UPDATED_WITH_ERROR_MESSAGE = "BT customer id(%s) updated with error: %s %s";

    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;

    @Override
    public BrainTreeUpdateCustomerResult perform(BrainTreeUpdateCustomerRequest brainTreeUpdateCustomerRequest) {
        try {
            Map<String, Object> result = makeGraphQlCall(DEFINITION_FILE_NAME,
                    createVariablesMap(brainTreeUpdateCustomerRequest));
            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 {
                List<ValidationError> errors = mapErrors.stream()
                        .map(stringObjectMap -> objectMapper.convertValue(stringObjectMap, ValidationError.class))
                        .collect(Collectors.toList());
                getLoggingHandler().handleValidationErrors(errors);
                return translateErrorResponse(brainTreeUpdateCustomerRequest.getId(), errors);
            }

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

    private BrainTreeUpdateCustomerResult translateResponse(Map<String, Object> data) {
        final BrainTreeUpdateCustomerResult result = new BrainTreeUpdateCustomerResult();
        BrainTreeUpdateCustomerPayload payload = objectMapper
                .convertValue(data.get(MUTATION_NAME), BrainTreeUpdateCustomerPayload.class);

        BrainTreeCustomer customer;

        if (payload != null) {
            customer = payload.getCustomer();
            if (customer != null) {
                result.setSuccess(true);
                result.setGraphQLId(customer.getId());
                result.setCustomer(customer);
                result.setTransactionStatus(TransactionStatus.ACCEPTED);
                result.setTransactionStatusDetails(TransactionStatusDetails.SUCCESFULL);
                getLoggingHandler().handleResult(customer);
            }
        }

        return result;
    }

    private BrainTreeUpdateCustomerResult translateErrorResponse(final String customerId,
                                                                 List<ValidationError> errors) {
        final BrainTreeUpdateCustomerResult response = new BrainTreeUpdateCustomerResult();
        response.setSuccess(false);

        if (errors != null && errors.size() > 0) {
            final ValidationError validationError = errors.get(0);
            getLoggingHandler().getLogger().info(
                    String.format(CUSTOMER_ID_UPDATED_WITH_ERROR_MESSAGE, customerId, validationError.getCode(),
                            validationError.getMessage()));

            if (validationError.getCode() != null) {
                response.setErrorCode(validationError.getCode().toString());
            }
            response.setErrorMessage(validationError.getMessage());
        }

        return response;
    }

    private Map<String, Object> createVariablesMap(final BrainTreeUpdateCustomerRequest request) {
        Map<String, Object> map = new HashMap<>();
        BrainTreeUpdateCustomerInput input = new BrainTreeUpdateCustomerInput();
        BrainTreeCustomerInput customer = new BrainTreeCustomerInput();

        if (StringUtils.isNotEmpty(request.getId())) {
            input.setCustomerId(brainTreeCustomerAccountService.getGraphQLIdForCustomer(request.getId()));
        }

        customer.setEmail(request.getEmail());
        customer.setFirstName(request.getFirstName());
        customer.setLastName(request.getLastName());
        customer.setCompany(request.getCompany());
        customer.setPhoneNumber(request.getPhone());

        input.setCustomer(customer);

        map.put(INPUT_PARAMETER, input);

        return map;
    }

    public BrainTreeCustomerAccountService getBrainTreeCustomerAccountService() {
        return brainTreeCustomerAccountService;
    }

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

}
