package com.braintree.facade.impl;

import com.braintree.exceptions.BrainTreeUnverifiedAccountPayPalException;
import com.braintree.facade.BrainTreeSetUpPasswordFacade;
import com.braintree.hybris.data.BrainTreeConnectWithPayPalEmailData;
import com.braintree.hybris.data.BrainTreeConnectWithPayPalUserData;
import com.braintree.service.BrainTreeConnectWithPayPalService;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.commercefacades.customer.CustomerFacade;
import de.hybris.platform.commercefacades.user.data.RegisterData;
import de.hybris.platform.commerceservices.customer.DuplicateUidException;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.UserService;
import org.apache.commons.lang3.StringUtils;
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.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String FIRST_EMAIL = "firstEmail@gmail.com";
    private static final String SECOND_EMAIL = "secondEmail@gmail.com";
    private static final String ACCESS_TOKEN = "accessToken";
    private static final String EMAIL = "email";
    private static final String FIRST_NAME = "firstName";
    private static final String LAST_NAME = "lastName";
    private static final String PAYER_ID = "payerID";

    @Mock
    private CustomerFacade customerFacade;
    @Mock
    private BrainTreeConnectWithPayPalService connectWithPayPalService;
    @Mock
    private ModelService modelService;
    @Mock
    private UserService userService;
    @Mock
    private BrainTreeSetUpPasswordFacade setUpPasswordFacade;
    @Spy
    @InjectMocks
    private DefaultBrainTreeRegistrationUserFacade unit;

    @Mock
    private CustomerModel customer;

    @Test
    public void shouldRegisterPayPalUser() throws DuplicateUidException {
        final BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setName(FIRST_NAME + " " + LAST_NAME);
        userData.setPayerId(PAYER_ID);

        when(connectWithPayPalService.getUserDataByAccessToken(ACCESS_TOKEN)).thenReturn(userData);
        doReturn(EMAIL).when(unit).getEmailFromPayPalUserData(userData);
        doNothing().when(setUpPasswordFacade).setPasswordForSingleAuthenticationActiveToCustomer(EMAIL, false);
        doNothing().when(unit).setPayerIdToUser(EMAIL, PAYER_ID);

        String result = unit.registerPayPalUser(ACCESS_TOKEN);

        assertEquals(PAYER_ID, result);
        verify(setUpPasswordFacade).setPasswordForSingleAuthenticationActiveToCustomer(EMAIL, false);
        verify(unit).setPayerIdToUser(EMAIL, PAYER_ID);
    }

    @Test(expected = DuplicateUidException.class)
    public void shouldThrowDuplicateUidExceptionDuringRegisterPayPalUser() throws DuplicateUidException {
        final BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setName(FIRST_NAME + " " + LAST_NAME);
        userData.setPayerId(PAYER_ID);

        doReturn(EMAIL).when(unit).getEmailFromPayPalUserData(userData);
        when(connectWithPayPalService.getUserDataByAccessToken(ACCESS_TOKEN)).thenReturn(userData);
        doThrow(new DuplicateUidException()).when(customerFacade).register(any(RegisterData.class));

        unit.registerPayPalUser(ACCESS_TOKEN);
    }

    @Test
    public void shouldReturnEmptyStringIfExceptionOccurDuringRegisterPayPalUser() throws DuplicateUidException {
        final BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setName(FIRST_NAME + " " + LAST_NAME);
        userData.setPayerId(PAYER_ID);

        doReturn(EMAIL).when(unit).getEmailFromPayPalUserData(userData);
        when(connectWithPayPalService.getUserDataByAccessToken(ACCESS_TOKEN)).thenReturn(userData);
        doThrow(new IllegalArgumentException()).when(customerFacade).register(any(RegisterData.class));

        String result = unit.registerPayPalUser(ACCESS_TOKEN);

        assertEquals(StringUtils.EMPTY, result);
    }

    @Test
    public void shouldSetPayerIdToUser() {
        when(userService.getUserForUID(EMAIL)).thenReturn(customer);

        unit.setPayerIdToUser(EMAIL, PAYER_ID);

        verify(customer).setPayPalPayerId(PAYER_ID);
        verify(modelService).save(customer);
    }

    @Test(expected = BrainTreeUnverifiedAccountPayPalException.class)
    public void shouldThrowBrainTreeConnectWithPayPalException() {
        BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setEmails(Collections.emptyList());

        unit.getEmailFromPayPalUserData(userData);
    }

    @Test(expected = BrainTreeUnverifiedAccountPayPalException.class)
    public void shouldThrowBrainTreeConnectWithPayPalExceptionWhenAccountUnverified() {
        BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setVerifiedAccount(Boolean.FALSE.toString());

        unit.getEmailFromPayPalUserData(userData);
    }

    @Test
    public void shouldComeBackTheFirstEmail() {
        List<BrainTreeConnectWithPayPalEmailData> emails = prepareEmailsForTesting();
        BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();
        userData.setEmails(emails);
        userData.setVerifiedAccount(Boolean.TRUE.toString());

        assertEquals("firstEmail@gmail.com".toLowerCase(), unit.getEmailFromPayPalUserData(userData));
    }

    private List<BrainTreeConnectWithPayPalEmailData> prepareEmailsForTesting() {
        List<BrainTreeConnectWithPayPalEmailData> emails = new ArrayList<>();
        BrainTreeConnectWithPayPalEmailData email1 = new BrainTreeConnectWithPayPalEmailData();
        email1.setValue(FIRST_EMAIL);
        email1.setConfirmed(Boolean.TRUE.toString());
        email1.setPrimary(Boolean.TRUE.toString());
        emails.add(email1);
        BrainTreeConnectWithPayPalEmailData email2 = new BrainTreeConnectWithPayPalEmailData();
        email2.setValue(SECOND_EMAIL);
        email2.setConfirmed(Boolean.FALSE.toString());
        email2.setPrimary(Boolean.TRUE.toString());
        emails.add(email2);
        return emails;
    }
}
