package com.braintree.commands.impl;

import com.braintree.command.result.BrainTreePaymentMethodNonceResult;
import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.enums.BraintreeThreeDSecureFlow;
import com.braintreegateway.BraintreeGateway;
import com.braintreegateway.PaymentMethodNonce;
import com.braintreegateway.PaymentMethodNonceGateway;
import com.braintreegateway.PaymentMethodNonceDetails;
import com.braintreegateway.PaymentMethodNonceRequest;
import com.braintreegateway.Result;
import com.braintreegateway.AuthenticationInsight;
import com.braintreegateway.ValidationError;
import com.braintreegateway.ValidationErrorCode;
import com.braintreegateway.ValidationErrors;
import de.hybris.bootstrap.annotations.UnitTest;
import org.apache.log4j.Logger;
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.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doReturn;

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

    private static final String GENERATED_NONCE = "generatedNonce";
    private static final String EMPTY = "";
    private static final String REGULATION_ENVIRONMENT = "regulationEnvironment";
    private static final String SCA_INDICATOR = "scaIndicator";
    private static final String MESSAGE = "message";
    private static final String METHOD_TOKEN = "methodToken";
    private static final String MERCHANT_ACCOUNT_ID = "merchantAccountId";

    @Mock
    private BraintreeGateway mockGateway;
    @Mock
    private BrainTreeConfigService mockConfigService;
    @Mock
    private Result<PaymentMethodNonce> mockResult;
    @Mock
    private DefaultBrainTreeLoggingHandler loggingHandler;
    @Mock
    private Logger logger;
    @Mock
    private PaymentMethodNonce mockPaymentMethodNonce;
    @Mock
    private PaymentMethodNonceGateway nonceGateway;
    @Spy
    @InjectMocks
    private DefaultBrainTreeCreatePaymentMethodNonceCommand createPaymentMethodNonceCommand;

    @Test
    public void testPerformWithAutoThreeDSecureFlow() {
        ValidationError error = mock(ValidationError.class);
        List<ValidationError> errorList = Arrays.asList(error);
        ValidationErrors validationError = mock(ValidationErrors.class);

        when(mockConfigService.getThreeDSecureFlow()).thenReturn(BraintreeThreeDSecureFlow.AUTO);
        when(mockConfigService.getCurrencyMerchantAccountId()).thenReturn(MERCHANT_ACCOUNT_ID);
        doReturn(mockGateway).when(createPaymentMethodNonceCommand).getBraintreeGateway();
        when(mockResult.getTarget()).thenReturn(mockPaymentMethodNonce);
        when(mockPaymentMethodNonce.getNonce()).thenReturn(GENERATED_NONCE);
        when(mockPaymentMethodNonce.getDetails()).thenReturn(new PaymentMethodNonceDetails(new HashMap<>()));
        when(mockPaymentMethodNonce.getAuthenticationInsight()).thenReturn(new AuthenticationInsight(getAuthenticationInsight()));
        when(mockGateway.paymentMethodNonce()).thenReturn(nonceGateway);
        when(nonceGateway.create(any(PaymentMethodNonceRequest.class))).thenReturn(mockResult);
        createPaymentMethodNonceCommand.setConfigService(mockConfigService);
        when(mockResult.getErrors()).thenReturn(validationError);
        when(error.getCode()).thenReturn(ValidationErrorCode.UNKNOWN_VALIDATION_ERROR);
        when(validationError.getAllDeepValidationErrors()).thenReturn(errorList);
        when(createPaymentMethodNonceCommand.getLoggingHandler()).thenReturn(loggingHandler);
        when(loggingHandler.getLogger()).thenReturn(logger);

        BrainTreePaymentMethodNonceResult result = createPaymentMethodNonceCommand.perform(METHOD_TOKEN);

        assertNotNull(result);
        assertEquals(GENERATED_NONCE, result.getNonce());
        assertEquals(REGULATION_ENVIRONMENT, result.getEnvironment());
        verify(mockGateway.paymentMethodNonce()).create(any(PaymentMethodNonceRequest.class));
    }

    @Test
    public void testPerformWithNonAutoThreeDSecureFlow() {
        when(mockConfigService.getThreeDSecureFlow()).thenReturn(BraintreeThreeDSecureFlow.DISABLED);
        doReturn(mockGateway).when(createPaymentMethodNonceCommand).getBraintreeGateway();
        when(mockResult.getTarget()).thenReturn(mockPaymentMethodNonce);
        when(mockPaymentMethodNonce.getNonce()).thenReturn(GENERATED_NONCE);
        when(mockPaymentMethodNonce.getDetails()).thenReturn(new PaymentMethodNonceDetails(new HashMap<>()));
        when(mockPaymentMethodNonce.getAuthenticationInsight()).thenReturn(new AuthenticationInsight(new HashMap<>()));
        when(mockGateway.paymentMethodNonce()).thenReturn(nonceGateway);
        when(nonceGateway.create(METHOD_TOKEN)).thenReturn(mockResult);
        createPaymentMethodNonceCommand.setConfigService(mockConfigService);

        BrainTreePaymentMethodNonceResult result = createPaymentMethodNonceCommand.perform(METHOD_TOKEN);

        assertNotNull(result);
        assertEquals(GENERATED_NONCE, result.getNonce());
        assertEquals(EMPTY, result.getEnvironment());
        verify(mockGateway.paymentMethodNonce()).create(METHOD_TOKEN);
    }

    public Map<String, Object> getAuthenticationInsight() {
        Map<String, Object> map = new HashMap<>();
        map.put(REGULATION_ENVIRONMENT, REGULATION_ENVIRONMENT);
        map.put(SCA_INDICATOR, SCA_INDICATOR);
        map.put(MESSAGE, MESSAGE);
        return map;
    }
}