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.AddressRequest;
import com.braintreegateway.BraintreeGateway;
import com.braintreegateway.Result;
import com.braintreegateway.ValidationErrors;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String DEFAULT_FIRST_NAME = "FirstName";
    private static final String DEFAULT_LAST_NAME = "LastName";
    private static final String DEFAULT_COMPANY = "DefaultCompany";
    private static final String DEFAULT_STREET_ADDRESS = "123 Hardcoded St";
    private static final String DEFAULT_EXTENDED_ADDRESS = "DefaultExtendedAddress";
    private static final String DEFAULT_ADDRESS_ID = "DefaultAddressId";
    private static final String DEFAULT_LOCALITY = "DefaultLocality";
    private static final String DEFAULT_REGION = "DefaultRegion";
    private static final String DEFAULT_POSTAL_CODE = "54321";
    private static final String DEFAULT_COUNTRY_CODE_ALPHA2 = "US";
    private static final String DEFAULT_CUSTOMER_ID = "customerId";

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

    @Spy
    @InjectMocks
    private DefaultBrainTreeUpdateAddressCommand updateAddressCommand;

    @Test
    public void shouldReturnSuccessWhenNoErrorOccurred() {
        BrainTreeAddressRequest addressRequest = createAddressRequest();

        when(mockResult.getErrors()).thenReturn(null);
        when(mockResult.getTarget()).thenReturn(mockAddress);
        doReturn(mockGateway).when(updateAddressCommand).getBraintreeGateway();
        when(mockGateway.address()).thenReturn(addressGateway);
        when(addressGateway.update(anyString(), anyString(), any(AddressRequest.class))).thenReturn(mockResult);

        BrainTreeAddressResult result = updateAddressCommand.perform(addressRequest);

        assertNotNull(result);
        verify(loggingHandler).handleAddressRequest(eq("[UPDATE BRAINTREE ADDRESS REQUEST]"), eq(addressRequest));
        verify(addressGateway).update(anyString(), anyString(), any(AddressRequest.class));
        verify(loggingHandler, never()).handleErrors(anyList());
        verify(loggingHandler).handleResult(eq("[UPDATE BRAINTREE ADDRESS RESULT]"), eq(mockAddress));
        assertEquals(mockResult.isSuccess(), result.isSuccess());
        assertEquals(mockResult.getMessage(), result.getErrorMessage());
    }

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

        when(mockResult.getErrors()).thenReturn(validationErrors);
        when(mockResult.getTarget()).thenReturn(mockAddress);
        doReturn(mockGateway).when(updateAddressCommand).getBraintreeGateway();
        doReturn(loggingHandler).when(updateAddressCommand).getLoggingHandler();
        when(mockGateway.address()).thenReturn(addressGateway);
        when(addressGateway.update(anyString(), anyString(), any(AddressRequest.class))).thenReturn(mockResult);

        BrainTreeAddressResult result = updateAddressCommand.perform(addressRequest);

        assertNotNull(result);
        verify(loggingHandler).handleAddressRequest(eq("[UPDATE BRAINTREE ADDRESS REQUEST]"), eq(addressRequest));
        verify(addressGateway).update(anyString(), anyString(), any(AddressRequest.class));
        verify(loggingHandler, times(2)).handleValidationErrors(validationErrors.getAllValidationErrors());
        verify(loggingHandler).handleResult(eq("[UPDATE BRAINTREE ADDRESS RESULT]"), eq(mockAddress));
        assertEquals(mockResult.isSuccess(), result.isSuccess());
        assertEquals(mockResult.getMessage(), result.getErrorMessage());
    }

    private BrainTreeAddressRequest createAddressRequest() {
        BrainTreeAddressRequest addressRequest = new BrainTreeAddressRequest(DEFAULT_CUSTOMER_ID);
        addressRequest.setFirstName(DEFAULT_FIRST_NAME);
        addressRequest.setLastName(DEFAULT_LAST_NAME);
        addressRequest.setCompany(DEFAULT_COMPANY);
        addressRequest.setStreetAddress(DEFAULT_STREET_ADDRESS);
        addressRequest.setExtendedAddress(DEFAULT_EXTENDED_ADDRESS);
        addressRequest.setLocality(DEFAULT_LOCALITY);
        addressRequest.setRegion(DEFAULT_REGION);
        addressRequest.setPostalCode(DEFAULT_POSTAL_CODE);
        addressRequest.setCountryCodeAlpha2(DEFAULT_COUNTRY_CODE_ALPHA2);
        addressRequest.setAddressId(DEFAULT_ADDRESS_ID);
        return addressRequest;
    }
}
