/*
 * Copyright 2018-2022 adorsys GmbH & Co KG
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see https://www.gnu.org/licenses/.
 *
 * This project is also available under a separate commercial license. You can
 * contact us at psd2@adorsys.com.
 */

package de.adorsys.xs2a.adapter.crealogix;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.adorsys.xs2a.adapter.api.RequestHeaders;
import de.adorsys.xs2a.adapter.api.Response;
import de.adorsys.xs2a.adapter.api.ResponseHeaders;
import de.adorsys.xs2a.adapter.api.http.HttpClient;
import de.adorsys.xs2a.adapter.api.http.HttpClientConfig;
import de.adorsys.xs2a.adapter.api.http.HttpClientFactory;
import de.adorsys.xs2a.adapter.api.http.Request;
import de.adorsys.xs2a.adapter.api.model.Aspsp;
import de.adorsys.xs2a.adapter.api.model.EmbeddedPreAuthorisationRequest;
import de.adorsys.xs2a.adapter.api.model.TokenResponse;
import de.adorsys.xs2a.adapter.crealogix.model.CrealogixValidationResponse;
import de.adorsys.xs2a.adapter.impl.http.BaseHttpClientConfig;
import de.adorsys.xs2a.adapter.impl.http.Xs2aHttpLogSanitizer;
import de.adorsys.xs2a.adapter.api.exception.AccessTokenException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.*;

class CrealogixEmbeddedPreAuthorisationServiceTest {

    private final HttpClient httpClient = mock(HttpClient.class);
    private final HttpClientFactory clientFactory = mock(HttpClientFactory.class);
    private final Aspsp aspsp = getAspsp();
    private CrealogixEmbeddedPreAuthorisationService authorisationService;

    private final HttpClientConfig clientConfig = new BaseHttpClientConfig(new Xs2aHttpLogSanitizer(), null, null);

    @BeforeEach
    void setUp() {
        when(clientFactory.getHttpClient(any())).thenReturn(httpClient);
        when(clientFactory.getHttpClientConfig()).thenReturn(clientConfig);

        authorisationService
            = new CrealogixEmbeddedPreAuthorisationService(CrealogixClient.DKB, aspsp, clientFactory);
    }

    @Test
    void getToken() {
        Response<CrealogixValidationResponse> psd2Response = mock(Response.class);
        TokenResponse psd2TokenResponse = new TokenResponse();
        psd2TokenResponse.setAccessToken("psd2");

        Request.Builder psd2Builder = mock(Request.Builder.class);

        doReturn(psd2Builder).when(httpClient).post("https://localhost:8443/foo/boo/token");
        doReturn(psd2Builder).when(psd2Builder).jsonBody(anyString());
        doReturn(psd2Builder).when(psd2Builder).headers(anyMap());
        doReturn(psd2Response).when(psd2Builder).send(any());
        doReturn(psd2Response).when(psd2Response).map(any());
        doReturn(psd2TokenResponse).when(psd2Response).getBody();

        TokenResponse token = authorisationService.getToken(new EmbeddedPreAuthorisationRequest(), RequestHeaders.fromMap(Collections.emptyMap()));

        assertThat(token.getAccessToken()).isNotBlank();
    }

    @ParameterizedTest
    @ValueSource(ints = {200, 201, 202})
    void responseHandler_successfulStatuses(int statusCode) throws JsonProcessingException {
        String stringBody = new ObjectMapper().writeValueAsString(new TokenResponse());
        InputStream inputStream = new ByteArrayInputStream(stringBody.getBytes());

        TokenResponse actualResponse
            = authorisationService.responseHandler(TokenResponse.class).apply(statusCode, inputStream, ResponseHeaders.emptyResponseHeaders());

        assertThat(actualResponse)
            .isNotNull()
            .isInstanceOf(TokenResponse.class);
    }

    @ParameterizedTest
    @ValueSource(ints = {302, 403, 500})
    void responseHandler_notSuccessfulStatuses(int statusCode) {
        HttpClient.ResponseHandler<TokenResponse> responseHandler = authorisationService.responseHandler(TokenResponse.class);
        ResponseHeaders responseHeaders = ResponseHeaders.emptyResponseHeaders();
        InputStream body = new ByteArrayInputStream("body".getBytes());

        assertThatThrownBy(() -> responseHandler.apply(statusCode, body, responseHeaders))
            .isInstanceOf(AccessTokenException.class);
    }

    private Aspsp getAspsp() {
        String url = "https://localhost:8443/";
        Aspsp aspsp = new Aspsp();
        aspsp.setIdpUrl(url);
        return aspsp;
    }
}
