package com.braintree.commands.impl;

import com.braintree.command.request.BrainTreeAddressRequest;
import com.braintree.command.result.BrainTreeAddressResult;
import com.braintreegateway.Address;

import com.braintreegateway.AddressGateway;
import com.braintreegateway.ValidationError;
import com.braintreegateway.ValidationErrorCode;
import com.braintreegateway.ValidationErrors;
import com.braintreegateway.BraintreeGateway;
import com.braintreegateway.Result;
import de.hybris.bootstrap.annotations.UnitTest;
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.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;

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

    private static final String ADDRESS_ID = "addressId";
    private static final String CUSTOMER_ID = "addressId";

    @Mock
    private BraintreeGateway mockGateway;
    @Mock
    private Result<Address> mockResult;
    @Mock
    private AddressGateway addressGateway;
    @Mock
    private DefaultBrainTreeLoggingHandler loggingHandler;

    @Spy
    @InjectMocks
    private DefaultBrainTreeRemoveAddressCommand removeAddressCommand;

    @Test
    public void shouldReturnSuccessWhenNoErrorOccurred() {
        BrainTreeAddressRequest addressRequest = createAddressRequest();
        Address mockAddress = mock(Address.class);

        doReturn(mockGateway).when(removeAddressCommand).getBraintreeGateway();
        when(mockGateway.address()).thenReturn(addressGateway);
        when(addressGateway.delete(anyString(), anyString())).thenReturn(mockResult);
        when(mockResult.getErrors()).thenReturn(null);
        when(mockResult.getTarget()).thenReturn(mockAddress);

        BrainTreeAddressResult result = removeAddressCommand.perform(addressRequest);

        assertNotNull(result);
        verify(removeAddressCommand.getLoggingHandler()).handleAddressRequest(eq("[REMOVE BRAINTREE ADDRESS REQUEST]"), eq(addressRequest));
        verify(addressGateway).delete(eq(addressRequest.getCustomerId()), eq(addressRequest.getAddressId()));
        verify(removeAddressCommand.getLoggingHandler(), never()).handleErrors(anyList());
        assertEquals(mockResult.isSuccess(), result.isSuccess());
        assertNull(result.getErrorCode());
        assertNull(result.getErrorMessage());
        assertEquals(mockAddress, result.getAddress());
    }

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

        doReturn(mockGateway).when(removeAddressCommand).getBraintreeGateway();
        when(mockGateway.address()).thenReturn(addressGateway);
        when(addressGateway.delete(anyString(), anyString())).thenReturn(mockResult);
        when(mockResult.getErrors()).thenReturn(validationError);
        when(mockResult.getTarget()).thenReturn(null);
        when(error.getCode()).thenReturn(ValidationErrorCode.UNKNOWN_VALIDATION_ERROR);
        when(validationError.getAllDeepValidationErrors()).thenReturn(errorList);

        BrainTreeAddressResult result = removeAddressCommand.perform(addressRequest);

        assertNotNull(result);
        verify(removeAddressCommand.getLoggingHandler()).handleAddressRequest(eq("[REMOVE BRAINTREE ADDRESS REQUEST]"), eq(addressRequest));
        verify(addressGateway).delete(eq(addressRequest.getCustomerId()), eq(addressRequest.getAddressId()));
        assertFalse(result.isSuccess());
        assertNull(result.getAddress());
        assertEquals(result.getErrorCode(), ValidationErrorCode.UNKNOWN_VALIDATION_ERROR.toString());
    }

    @Test
    public void shouldReturnResponseWithoutDeepValidationErrors() {
        BrainTreeAddressRequest addressRequest = createAddressRequest();
        ValidationErrors validationError = mock(ValidationErrors.class);

        doReturn(mockGateway).when(removeAddressCommand).getBraintreeGateway();
        when(mockGateway.address()).thenReturn(addressGateway);
        when(addressGateway.delete(anyString(), anyString())).thenReturn(mockResult);
        when(mockResult.getErrors()).thenReturn(validationError);
        when(mockResult.getTarget()).thenReturn(null);
        when(validationError.getAllDeepValidationErrors()).thenReturn(null);

        BrainTreeAddressResult result = removeAddressCommand.perform(addressRequest);

        assertNotNull(result);
        verify(removeAddressCommand.getLoggingHandler()).handleAddressRequest(eq("[REMOVE BRAINTREE ADDRESS REQUEST]"), eq(addressRequest));
        verify(addressGateway).delete(eq(addressRequest.getCustomerId()), eq(addressRequest.getAddressId()));
        assertFalse(result.isSuccess());
        assertNull(result.getAddress());
        assertNull(result.getErrorCode());

    }

    private BrainTreeAddressRequest createAddressRequest() {
        BrainTreeAddressRequest addressRequest = new BrainTreeAddressRequest(CUSTOMER_ID);
        addressRequest.setAddressId(ADDRESS_ID);
        return addressRequest;
    }
}
