package com.braintree.facade.impl;

import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.customer.service.BrainTreeCustomerAccountService;
import com.braintree.enums.BrainTreePaymentMethod;
import com.braintree.facade.BraintreeConnectWithPayPalNotificationFacade;
import com.braintree.hybris.data.BrainTreeConnectWithPayPalAddressData;
import com.braintree.hybris.data.BrainTreeConnectWithPayPalUserData;
import com.braintree.model.BrainTreePaymentInfoModel;
import com.braintree.service.BrainTreeConnectWithPayPalService;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.acceleratorservices.urlresolver.SiteBaseUrlResolutionService;
import de.hybris.platform.basecommerce.model.site.BaseSiteModel;
import de.hybris.platform.core.model.c2l.CountryModel;
import de.hybris.platform.core.model.c2l.RegionModel;
import de.hybris.platform.core.model.user.AddressModel;
import de.hybris.platform.core.model.user.CustomerModel;
import de.hybris.platform.servicelayer.i18n.CommonI18NService;
import de.hybris.platform.servicelayer.model.ModelService;
import de.hybris.platform.servicelayer.user.UserService;
import de.hybris.platform.site.BaseSiteService;
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.Collections;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String ACCESS_TOKEN = "accessToken";
    private static final String AUTHORIZATION_TOKEN = "authorizationToken";
    private static final String PAYER_ID = "payerId";
    private static final String EMAIL = "email";
    private static final String US = "US";
    private static final String AL = "AL";
    private static final String ISO_CODE = "12345";
    private static final String FIRST_NAME = "firstName";
    private static final String LAST_NAME = "lastName";
    private static final String POSTAL_CODE = "postalCode";
    private static final String STREET_LINE_1 = "streetLine1";
    private static final String TOWN = "town";
    private static final String STREET_LINE_1_DIFFERENT = "streetLine1Different";
    private static final String UID = "uid";

    @Mock
    private BrainTreeConnectWithPayPalService brainTreeConnectWithPayPalService;
    @Mock
    private BrainTreeCustomerAccountService brainTreeCustomerAccountService;
    @Mock
    private UserService userService;
    @Mock
    private ModelService modelService;
    @Mock
    private CommonI18NService commonI18NService;
    @Mock
    private BrainTreeConfigService brainTreeConfigService;
    @Mock
    private BraintreeConnectWithPayPalNotificationFacade braintreeConnectWithPayPalNotificationFacade;

    @Spy
    @InjectMocks
    private DefaultBrainTreeConnectWithPayPalFacade unit;

    @Mock
    private CustomerModel customerModel;
    @Mock
    private AddressModel addressModel;
    @Mock
    private AddressModel newAddress;
    @Mock
    private CountryModel countryModel;
    @Mock
    private RegionModel regionModel;
    @Mock
    private BrainTreePaymentInfoModel paymentInfoModel;
    @Mock
    private DefaultBrainTreeCheckoutFacade acceleratorCheckoutFacade;

    @Test
    public void shouldReturnUserDataByAccessToken() {
        final BrainTreeConnectWithPayPalUserData userData = new BrainTreeConnectWithPayPalUserData();

        when(brainTreeConnectWithPayPalService.getUserDataByAccessToken(ACCESS_TOKEN)).thenReturn(userData);

        unit.getUserDataByAccessToken(ACCESS_TOKEN);

        verify(brainTreeConnectWithPayPalService).getUserDataByAccessToken(ACCESS_TOKEN);
    }

    @Test
    public void shouldGetAccessToken() {
        when(brainTreeConnectWithPayPalService.getAccessToken()).thenReturn(ACCESS_TOKEN);

        unit.getAccessToken();

        verify(brainTreeConnectWithPayPalService).getAccessToken();
    }

    @Test
    public void shouldExchangeAuthorizationCodeToAccessToken() {
        when(brainTreeConnectWithPayPalService.exchangeAuthorizationCodeToAccessToken(AUTHORIZATION_TOKEN)).thenReturn(ACCESS_TOKEN);

        unit.exchangeAuthorizationCodeToAccessToken(AUTHORIZATION_TOKEN);

        verify(brainTreeConnectWithPayPalService).exchangeAuthorizationCodeToAccessToken(AUTHORIZATION_TOKEN);
    }

    @Test
    public void shouldSetAccessTokenToCustomer() {
        unit.setAccessTokenForCustomer(ACCESS_TOKEN, PAYER_ID);

        verify(brainTreeCustomerAccountService).setAccessTokenForCustomer(ACCESS_TOKEN, PAYER_ID);
    }

    @Test
    public void shouldSetDefaultAccessToken() {
        unit.setDefaultAccessToken(PAYER_ID);

        verify(brainTreeCustomerAccountService).setDefaultAccessToken(PAYER_ID);
    }

    @Test
    public void shouldReturnTrueWhenAddressPresent() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();
        addressData.setCountry(ISO_CODE);
        addressData.setRegion(ISO_CODE);
        addressData.setStreetAddress(STREET_LINE_1);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLocality(TOWN);

        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(customerModel.getAddresses()).thenReturn(List.of(addressModel));
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getRegion()).thenReturn(regionModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getLine1()).thenReturn(STREET_LINE_1);
        when(addressModel.getTown()).thenReturn(TOWN);
        when(countryModel.getIsocode()).thenReturn(ISO_CODE);
        when(regionModel.getIsocodeShort()).thenReturn(ISO_CODE);

        boolean result = unit.isPayPalAddressPresent(EMAIL, addressData);

        assertTrue(result);
    }

    @Test
    public void shouldReturnTrueWhenBothRegionAbsent() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();
        addressData.setCountry(ISO_CODE);
        addressData.setStreetAddress(STREET_LINE_1);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLocality(TOWN);

        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(customerModel.getAddresses()).thenReturn(List.of(addressModel));
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getPostalcode()).thenReturn(POSTAL_CODE);
        when(addressModel.getLine1()).thenReturn(STREET_LINE_1);
        when(addressModel.getTown()).thenReturn(TOWN);
        when(countryModel.getIsocode()).thenReturn(ISO_CODE);

        boolean result = unit.isPayPalAddressPresent(EMAIL, addressData);

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseWhenOneReginAbsent() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();
        addressData.setCountry(ISO_CODE);
        addressData.setRegion(ISO_CODE);
        addressData.setStreetAddress(STREET_LINE_1);
        addressData.setPostalCode(POSTAL_CODE);
        addressData.setLocality(TOWN);

        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(customerModel.getAddresses()).thenReturn(List.of(addressModel));
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(countryModel.getIsocode()).thenReturn(ISO_CODE);

        boolean result = unit.isPayPalAddressPresent(EMAIL, addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseWhenStreetDifferent() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();
        addressData.setCountry(ISO_CODE);
        addressData.setRegion(ISO_CODE);
        addressData.setStreetAddress(STREET_LINE_1_DIFFERENT);

        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(customerModel.getAddresses()).thenReturn(List.of(addressModel));
        when(addressModel.getCountry()).thenReturn(countryModel);
        when(addressModel.getRegion()).thenReturn(regionModel);
        when(countryModel.getIsocode()).thenReturn(ISO_CODE);
        when(regionModel.getIsocodeShort()).thenReturn(ISO_CODE);

        boolean result = unit.isPayPalAddressPresent(EMAIL, addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnFalseWhenUserDoesNotHaveAddresses() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();

        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(customerModel.getAddresses()).thenReturn(Collections.emptyList());

        boolean result = unit.isPayPalAddressPresent(EMAIL, addressData);

        assertFalse(result);
    }

    @Test
    public void shouldReturnTrueIfPayPalPaymentMethodPresent() {
        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel));
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.PAYPALACCOUNT.getCode());
        when(paymentInfoModel.getPayer()).thenReturn(EMAIL);

        boolean result = unit.isPayPalPaymentMethodPresent(EMAIL);

        assertTrue(result);
    }

    @Test
    public void shouldReturnFalseIfPayPalPaymentMethodAbsent() {
        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(brainTreeCustomerAccountService.getBrainTreePaymentInfos(customerModel, true)).thenReturn(List.of(paymentInfoModel));
        when(paymentInfoModel.getPaymentProvider()).thenReturn(BrainTreePaymentMethod.APPLEPAYCARD.getCode());

        assertFalse(unit.isPayPalPaymentMethodPresent(EMAIL));
    }

    @Test
    public void shouldReturnTrueIfPayPalPaymentMethodPresentForConnectWithPayPal() {
        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isStoreInVault()).thenReturn(true);
        doReturn(false).when(acceleratorCheckoutFacade).isPayPalLimitedDuringConnect(EMAIL);
        doReturn(true).when(unit).isPayPalPaymentMethodPresent(EMAIL);

        boolean result = unit.isPayPalPaymentMethodPresentForConnectWithPayPal(EMAIL);

        assertTrue(result);
    }

    @Test
    public void shouldReturnTrueWhenAddingPayPalOnLimit() {
        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isStoreInVault()).thenReturn(true);
        doReturn(true).when(acceleratorCheckoutFacade).isPayPalLimitedDuringConnect(EMAIL);

        boolean result = unit.isPayPalPaymentMethodPresentForConnectWithPayPal(EMAIL);

        assertTrue(result);
    }

    @Test
    public void shouldReturnTrueWhenPayPalPaymentMethodDisabled() {
        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(false);
        assertTrue(unit.isPayPalPaymentMethodPresentForConnectWithPayPal(EMAIL));
    }

    @Test
    public void shouldReturnTrueWhenStoreInVaultDisabled() {
        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isStoreInVault()).thenReturn(false);

        assertTrue(unit.isPayPalPaymentMethodPresentForConnectWithPayPal(EMAIL));
    }

    @Test
    public void shouldSavePayPalPaymentInfoMustReturnTrueTest() {
        when(brainTreeConfigService.isPayPalEnabled()).thenReturn(true);
        when(brainTreeConfigService.isStoreInVault()).thenReturn(true);

        assertTrue(unit.shouldSavePayPalPaymentInfo());
    }

    @Test
    public void shouldAddAddressForNewUser() {
        final BrainTreeConnectWithPayPalAddressData addressData = new BrainTreeConnectWithPayPalAddressData();
        addressData.setCountry(US);
        addressData.setRegion(AL);
        when(modelService.create(AddressModel.class)).thenReturn(newAddress);
        when(commonI18NService.getCountry(US)).thenReturn(countryModel);
        when(userService.getUserForUID(EMAIL)).thenReturn(customerModel);
        when(countryModel.getIsocode()).thenReturn(ISO_CODE);
        when(customerModel.getName()).thenReturn(FIRST_NAME + " " + LAST_NAME);

        unit.addAddressForNewUser(addressData, EMAIL);

        verify(brainTreeCustomerAccountService).saveAddressEntry(customerModel, newAddress);
    }

    @Test
    public void shouldUnlinkConnectWithPayPalAccount() {
        when(userService.getCurrentUser()).thenReturn(customerModel);
        when(customerModel.getPayPalPayerId()).thenReturn(PAYER_ID);
        when(customerModel.getUid()).thenReturn(UID);

        boolean result = unit.unlinkConnectWithPayPalAccount();

        assertTrue(result);
        verify(customerModel).setPayPalPayerId(null);
        verify(modelService).save(customerModel);
        verify(braintreeConnectWithPayPalNotificationFacade).sendUnlinkCustomerEmailNotification(UID);
    }

    @Test
    public void shouldNotUnlinkConnectWithPayPalAccountIfCustomerNull() {
        boolean result = unit.unlinkConnectWithPayPalAccount();

        assertFalse(result);
        verify(customerModel, never()).setPayPalPayerId(null);
        verify(modelService, never()).save(any());
        verify(braintreeConnectWithPayPalNotificationFacade, never()).sendUnlinkCustomerEmailNotification(any());
    }

    @Test
    public void shouldNotUnlinkConnectWithPayPalAccountIfCustomerDoesNoptHavePayerId() {
        when(userService.getCurrentUser()).thenReturn(customerModel);

        boolean result = unit.unlinkConnectWithPayPalAccount();

        assertFalse(result);
        verify(customerModel, never()).setPayPalPayerId(null);
        verify(modelService, never()).save(any());
        verify(braintreeConnectWithPayPalNotificationFacade, never()).sendUnlinkCustomerEmailNotification(any());
    }

}
