package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeDeletePaymentMethodRequest;
import com.braintree.command.result.BrainTreePaymentMethodResult;
import com.braintree.commands.BrainTreeDeletePaymentMethodCommand;
import com.braintree.commands.impl.AbstractCommand;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeDeletePaymentMethodFromVaultInput;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeErrorDefinition;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import com.braintreegateway.exceptions.NotFoundException;
import de.hybris.platform.payment.AdapterException;

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_ERRORS;
import static de.hybris.platform.servicelayer.util.ServicesUtil.validateParameterNotNullStandardMessage;

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

    private static final String DEFINITION_FILE_NAME = "deletePaymentMethod";
    private static final String REMOVE_PAYMENT_METHOD_REQUEST_PARAMETER = "Remove payment method request";
    private static final String PAYMENT_METHOD_TOKEN_DELETING_WITH_ERROR_LOG_MESSAGE =
            "BT payment method token(%s) deleting with error: %s %s";
    private static final String PAYMENT_METHOD_WITH_TOKEN_NOT_FOUND_ERROR_LOG_MESSAGE =
            "Payment Method with token=%s not Found! Error %s";
    private static final String PAYMENT_METHOD_WITH_TOKEN_ID_NOT_FOUND_MESSAGE =
            "Payment Method with token id=%s not Found!";

    private BraintreePaymentInfoService paymentInfoService;

    @Override
    public BrainTreePaymentMethodResult perform(BrainTreeDeletePaymentMethodRequest request) {
        validateParameterNotNullStandardMessage(REMOVE_PAYMENT_METHOD_REQUEST_PARAMETER, request);
        try {
            Map<String, Object> result = makeGraphQlCall(DEFINITION_FILE_NAME, createVariablesMap(request));
            ArrayList<Map<String, Object>> mapErrors = (ArrayList<Map<String, Object>>) result.get(RESULT_ERRORS);

            if (mapErrors == null) {
                return new BrainTreePaymentMethodResult(true);
            } else {
                List<BrainTreeErrorDefinition> errors = mapErrors.stream()
                        .map(stringObjectMap -> objectMapper.convertValue(stringObjectMap, BrainTreeErrorDefinition.class))
                        .collect(Collectors.toList());
                return translateErrorResponse(request.getToken(), errors);
            }
        } catch (final NotFoundException notFoundException) {
            return translateNotFoundResponse(request, notFoundException);
        } catch (final Exception exception) {
            throw new AdapterException(exception.getMessage(), exception);
        }
    }

    private BrainTreePaymentMethodResult translateErrorResponse(final String token,
                                                                List<BrainTreeErrorDefinition> errors) {
        final BrainTreePaymentMethodResult response = new BrainTreePaymentMethodResult(false);

        if (!errors.isEmpty()) {
            final BrainTreeErrorDefinition errorDefinition = errors.get(0);
            getLoggingHandler().getLogger().info(
                    String.format(PAYMENT_METHOD_TOKEN_DELETING_WITH_ERROR_LOG_MESSAGE, token,
                            errorDefinition.getExtensions().getLegacyCode(),
                            errorDefinition.getMessage()));

            if (errorDefinition.getExtensions().getLegacyCode() != null) {
                response.setErrorCode(errorDefinition.getExtensions().getLegacyCode());
            }
            response.setErrorMessage(errorDefinition.getMessage());
        }

        return response;
    }

    private BrainTreePaymentMethodResult translateNotFoundResponse(final BrainTreeDeletePaymentMethodRequest request,
                                                                   final NotFoundException notFoundException) {
        getLoggingHandler().getLogger()
                .info(String.format(PAYMENT_METHOD_WITH_TOKEN_NOT_FOUND_ERROR_LOG_MESSAGE, request.getToken(),
                        notFoundException.getMessage()));

        final BrainTreePaymentMethodResult brainTreeCustomerResult = new BrainTreePaymentMethodResult();
        brainTreeCustomerResult.setSuccess(Boolean.FALSE.booleanValue());
        brainTreeCustomerResult
                .setErrorMessage(String.format(PAYMENT_METHOD_WITH_TOKEN_ID_NOT_FOUND_MESSAGE, request.getToken()));
        return brainTreeCustomerResult;
    }

    private Map<String, Object> createVariablesMap(final BrainTreeDeletePaymentMethodRequest request) {
        Map<String, Object> map = new HashMap<>();
        BrainTreeDeletePaymentMethodFromVaultInput input = new BrainTreeDeletePaymentMethodFromVaultInput();
        input.setPaymentMethodId(paymentInfoService.getGraphQLTokenForPaymentMethod(request.getToken()));
        map.put(INPUT_PARAMETER, input);
        return map;
    }

    public void setPaymentInfoService(BraintreePaymentInfoService paymentInfoService) {
        this.paymentInfoService = paymentInfoService;
    }

}
