package com.braintree.commands.impl;

import com.braintree.configuration.service.BrainTreeConfigService;
import com.braintree.hybris.data.BrainTreeConnectWithPayPalAccessTokenData;
import de.hybris.bootstrap.annotations.UnitTest;
import de.hybris.platform.payment.AdapterException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

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

    private static final String AUTHORIZATION_CODE = "authCode";
    private static final String EXCHANGE_ENDPOINT_URL = "oauth2/token";
    private static final String API_URL = "http://api.example.com";
    private static final String CLIENT_ID = "clientID";
    private static final String SECRET_KEY = "secretKey";
    private static final String GRANT_TYPE = "grant_type";
    private static final String GRANT_TYPE_VALUE = "authorization_code";
    private static final String CODE = "code";
    private static final String BASIC_AUTH_HEADER = "Authorization";
    private static final String BASIC_AUTH_PREFIX = "Basic ";
    private static final String BASE64_ENCODED_AUTH_HEADER = "Y2xpZW50SUQ6c2VjcmV0S2V5";

    @Mock
    private RestTemplate restTemplate;

    @Mock
    private BrainTreeConfigService mockConfigService;

    @InjectMocks
    private DefaultBrainTreeConnectWithPayPalExchangeCodeToAccessTokenCommand exchangeCodeToAccessTokenCommand;

    @Before
    public void setUp() {
        when(mockConfigService.getPayPalApiUrl()).thenReturn(API_URL);
        when(mockConfigService.getClientID()).thenReturn(CLIENT_ID);
        when(mockConfigService.getSecretKey()).thenReturn(SECRET_KEY);
    }

    @Test
    public void shouldReturnAccessTokenDataOnSuccessfulRequest() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + BASE64_ENCODED_AUTH_HEADER);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add(GRANT_TYPE, GRANT_TYPE_VALUE);
        map.add(CODE, AUTHORIZATION_CODE);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(map, headers);
        BrainTreeConnectWithPayPalAccessTokenData mockAccessTokenData = new BrainTreeConnectWithPayPalAccessTokenData();
        ResponseEntity<BrainTreeConnectWithPayPalAccessTokenData> mockResponse = new ResponseEntity<>(mockAccessTokenData, HttpStatus.OK);

        when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any(Class.class)))
                .thenReturn(mockResponse);

        BrainTreeConnectWithPayPalAccessTokenData result = exchangeCodeToAccessTokenCommand.perform(AUTHORIZATION_CODE);

        verify(restTemplate).postForEntity(API_URL + EXCHANGE_ENDPOINT_URL, httpEntity, BrainTreeConnectWithPayPalAccessTokenData.class);
        assertEquals(mockAccessTokenData, result);
    }

    @Test(expected = AdapterException.class)
    public void shouldThrowAdapterExceptionOnRequestFailure() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set(BASIC_AUTH_HEADER, BASIC_AUTH_PREFIX + BASE64_ENCODED_AUTH_HEADER);

        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add(GRANT_TYPE, GRANT_TYPE_VALUE);
        map.add(CODE, AUTHORIZATION_CODE);

        when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any(Class.class)))
                .thenThrow(new RuntimeException("Some error occurred during request"));

        exchangeCodeToAccessTokenCommand.perform(AUTHORIZATION_CODE);
    }
}