package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeSubmitForSettlementTransactionRequest;
import com.braintree.commands.impl.AbstractCaptureCommand;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeCaptureTransactionInput;
import com.braintree.hybris.data.graphql.commands.request.BrainTreeCaptureTransactionOptionsInput;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransaction;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeTransactionPayload;
import com.braintree.service.BrainTreeDescriptorService;
import com.braintree.transaction.service.BrainTreeTransactionService;
import de.hybris.platform.payment.AdapterException;
import de.hybris.platform.payment.commands.CaptureCommand;
import de.hybris.platform.payment.commands.result.CaptureResult;
import de.hybris.platform.payment.dto.TransactionStatus;
import de.hybris.platform.payment.dto.TransactionStatusDetails;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Currency;
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 AbstractCaptureCommand, implements CaptureCommand and is used in GraphQL API.
 */
public class DefaultBrainTreeGraphQLCaptureCommand extends AbstractCaptureCommand implements CaptureCommand {

    private static final String DEFINITION_FILE_NAME = "captureTransaction";
    private static final String MUTATION_NAME = "captureTransaction";
    private static final String CAN_NOT_FIND_MUTATION_FOR_CAPTURE_ERROR_MESSAGE =
            "[GraphQL Capture Command] Can not find mutation for capture call";
    private static final String CAPTURE_TRANSACTION = "[CAPTURE TRANSACTION] ";

    private BrainTreeTransactionService brainTreeTransactionService;
    private BrainTreeDescriptorService brainTreeDescriptorService;

    @Override
    protected CaptureResult makeApiCall(BrainTreeSubmitForSettlementTransactionRequest 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 translateResponse((Map<String, Object>) result.get(RESULT_DATA), request.getTransactionId());
            } else {
                throw new AdapterException(getFistErrorMessage(mapErrors));
            }

        } catch (IOException e) {
            throw new AdapterException(CAN_NOT_FIND_MUTATION_FOR_CAPTURE_ERROR_MESSAGE, e);
        }
    }

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

        final CaptureResult result = new CaptureResult();
        result.setTransactionStatus(TransactionStatus.REJECTED);
        result.setTransactionStatusDetails(TransactionStatusDetails.BANK_DECLINE);
        if (payload != null) {
            BrainTreeTransaction transaction = payload.getTransaction();
            if (transaction != null && transaction.getStatus() != null && transaction.getStatus().isSuccess()) {
                result.setTotalAmount(new BigDecimal(transaction.getAmount().getValue()));
                result.setCurrency(Currency.getInstance(transaction.getAmount().getCurrencyCode()));
                result.setMerchantTransactionCode(transaction.getMerchantAccountId());
                result.setRequestId(transaction.getId());
                result.setRequestToken(transactionIdForCapture);
                result.setTransactionStatus(TransactionStatus.ACCEPTED);
                result.setTransactionStatusDetails(TransactionStatusDetails.SUCCESFULL);
            }
            getLoggingHandler().handleResult(CAPTURE_TRANSACTION, transaction);
        }

        return result;
    }

    private Map<String, Object> createVariablesMap(BrainTreeSubmitForSettlementTransactionRequest request) {
        Map<String, Object> map = new HashMap<>();
        final BrainTreeCaptureTransactionInput input = new BrainTreeCaptureTransactionInput();
        input.setTransactionId(brainTreeTransactionService.getGraphQLIdFromTransaction(request.getTransactionId()));
        final BrainTreeCaptureTransactionOptionsInput transactionOptionsInput = new BrainTreeCaptureTransactionOptionsInput();

        if (request.getOrderId() != null) {
            transactionOptionsInput.setOrderId(request.getOrderId());
        }
        if (request.getAmount() != null) {
            transactionOptionsInput.setAmount(request.getAmount());
        }

        String paymentType = request.getPaymentProvider();
        if (brainTreeDescriptorService.isDescriptorAvailable(paymentType)) {
            transactionOptionsInput.setDescriptor(brainTreeDescriptorService.getTransactionDescriptor(paymentType));
        }

        input.setTransaction(transactionOptionsInput);
        map.put(INPUT_PARAMETER, input);
        return map;
    }

    public BrainTreeTransactionService getBrainTreeTransactionService() {
        return brainTreeTransactionService;
    }

    public void setBrainTreeTransactionService(
            BrainTreeTransactionService brainTreeTransactionService) {
        this.brainTreeTransactionService = brainTreeTransactionService;
    }

    public void setBrainTreeDescriptorService(BrainTreeDescriptorService brainTreeDescriptorService) {
        this.brainTreeDescriptorService = brainTreeDescriptorService;
    }
}
