package com.braintree.graphql.commands.impl;

import com.braintree.command.request.BrainTreeCreatePaymentMethodRequest;
import com.braintree.command.request.BrainTreeOptionsRequest;
import com.braintree.command.result.BrainTreeCreatePaymentMethodResult;
import com.braintree.commands.impl.DefaultBrainTreeLoggingHandler;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodDetails;
import com.braintree.hybris.data.graphql.commands.response.BrainTreePaymentMethodOrigin;
import com.braintree.hybris.data.graphql.commands.response.BrainTreeVaultPaymentMethodPayload;
import com.braintree.hybris.data.graphql.commands.response.BraintreePaymentMethodData;
import com.braintree.method.BrainTreePaymentService;
import com.braintree.payment.info.service.BraintreePaymentInfoService;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.payment.AdapterException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


@UnitTest
@RunWith(MockitoJUnitRunner.class)
public class DefaultBrainTreeGraphQLCreateUsBankAccountPaymentMethodCommandTest {

    private static final String TEST_ID = "testId";
    private static final String TEST_LEGACY_ID = "testLegacyId";
    private static final String US_BANK_ACCOUNT_DETAILS = "UsBankAccountDetails";
    private static final String US_BANK_ACCOUNT = "UsBankAccount";
    private static final String US_BANK_ACCOUNT_PROVIDER = "us_bank_account_provider";
    private static final String VAL_0000 = "0000";
    private static final String VAL_0000000000 = "0000000000";

    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;
    @Mock
    private BrainTreePaymentService brainTreePaymentService;
    @Mock
    private BrainTreeConfigService brainTreeConfigService;
    @Mock
    private BraintreePaymentInfoService braintreePaymentInfoService;
    @Mock
    private DefaultBrainTreeLoggingHandler loggingHandler;
    @Mock
    protected ObjectMapper objectMapper;
    @Mock
    private ArrayList<Map<String, Object>> mapErrors;

    @Spy
    @InjectMocks
    private DefaultBrainTreeGraphQLCreateUsBankAccountPaymentMethodCommand command;

    @Test
    public void performTest() throws IOException {
        BrainTreeCreatePaymentMethodRequest request = mock(BrainTreeCreatePaymentMethodRequest.class);
        BrainTreeOptionsRequest optionsRequest = mock(BrainTreeOptionsRequest.class);
        BrainTreeVaultPaymentMethodPayload payload = mock(BrainTreeVaultPaymentMethodPayload.class);
        BraintreePaymentMethodData paymentMethod = mock(BraintreePaymentMethodData.class);
        BrainTreePaymentMethodDetails paymentMethodDetails = mock(BrainTreePaymentMethodDetails.class);
        BrainTreePaymentMethodOrigin paymentMethodOrigin = mock(BrainTreePaymentMethodOrigin.class);

        doReturn(createVerificationMap()).when(command).makeGraphQlCall(any(), any());
        when(request.getOptions()).thenReturn(optionsRequest);
        when(request.getCustomerId()).thenReturn(VAL_0000000000);
        when(payload.getPaymentMethod()).thenReturn(paymentMethod);
        when(paymentMethod.getDetails()).thenReturn(paymentMethodDetails);
        when(paymentMethodDetails.isVerified()).thenReturn(true);
        when(paymentMethodDetails.get__typename()).thenReturn(US_BANK_ACCOUNT_PROVIDER);
        when(paymentMethodDetails.getOrigin()).thenReturn(paymentMethodOrigin);
        when(paymentMethodDetails.getBrandCode()).thenReturn(US_BANK_ACCOUNT);
        when(paymentMethodOrigin.getType()).thenReturn(US_BANK_ACCOUNT);
        when(braintreePaymentInfoService.getPaymentProviderByDetailsAndOrigin(any(), any())).thenReturn(US_BANK_ACCOUNT);

        doReturn(payload).when(objectMapper).convertValue(any(), eq(BrainTreeVaultPaymentMethodPayload.class));

        BrainTreeCreatePaymentMethodResult perform = command.perform(request);

        Assert.assertTrue(perform.getVerified());
        verify(brainTreeCustomerAccountService).getGraphQLIdForCustomer(VAL_0000000000);
        verify(braintreePaymentInfoService).getPaymentProviderByDetailsAndOrigin(US_BANK_ACCOUNT_PROVIDER, US_BANK_ACCOUNT);
        verify(braintreePaymentInfoService).getImageSourceByPaymentProviderAndCardType(US_BANK_ACCOUNT, US_BANK_ACCOUNT);
    }


    private Map<String, Object> createVerificationMap() {
        Map<String, Object> result = new LinkedHashMap<>();
        Map<String, Object> data = new LinkedHashMap<>();
        Map<String, Object> vaultUsBankAccount = new LinkedHashMap<>();
        Map<String, Object> paymentMethod = new LinkedHashMap<>();
        Map<String, Object> details = new LinkedHashMap<>();

        result.put("data", data);
        data.put("vaultUsBankAccount", vaultUsBankAccount);
        vaultUsBankAccount.put("paymentMethod", paymentMethod);
        paymentMethod.put("id", TEST_ID);
        paymentMethod.put("legacyId", TEST_LEGACY_ID);
        paymentMethod.put("details", details);
        details.put("__typename", US_BANK_ACCOUNT_DETAILS);
        details.put("last4", VAL_0000);
        details.put("routingNumber", VAL_0000000000);
        details.put("verified", true);

        return result;
    }

    private Map<String, Object> createVerificationMapWithErrors() {
        Map<String, Object> result = new LinkedHashMap<>();

        result.put("errors", mapErrors);

        return result;
    }

    @Test(expected = AdapterException.class)
    public void performTestWithError() throws IOException {
        BrainTreeCreatePaymentMethodRequest request = mock(BrainTreeCreatePaymentMethodRequest.class);
        BrainTreeOptionsRequest optionsRequest = mock(BrainTreeOptionsRequest.class);

        doReturn(createVerificationMapWithErrors()).when(command).makeGraphQlCall(any(), any());
        when(request.getOptions()).thenReturn(optionsRequest);

        command.perform(request);
    }
}
