package org.baeldung.httpclient.advancedconfig;


import com.github.tomakehurst.wiremock.junit.WireMockRule;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.junit.Rule;
import org.junit.Test;

import java.io.IOException;

import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.junit.Assert.assertEquals;

public class HttpClientAdvancedConfigurationIntegrationTest {

    @Rule
    public WireMockRule serviceMock = new WireMockRule(8089);

    @Rule
    public WireMockRule proxyMock = new WireMockRule(8090);

    @Test
    public void givenClientWithCustomUserAgentHeader_whenExecuteRequest_shouldReturn200() throws IOException {
        //given
        String userAgent = "BaeldungAgent/1.0";
        serviceMock.stubFor(get(urlEqualTo("/detail"))
                .withHeader("User-Agent", equalTo(userAgent))
                .willReturn(aResponse()
                        .withStatus(200)));

        HttpClient httpClient = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet("http://localhost:8089/detail");
        httpGet.setHeader(HttpHeaders.USER_AGENT, userAgent);

        //when
        HttpResponse response = httpClient.execute(httpGet);

        //then
        assertEquals(response.getStatusLine().getStatusCode(), 200);
    }

    @Test
    public void givenClientThatSendDataInBody_whenSendXmlInBody_shouldReturn200() throws IOException {
        //given
        String xmlBody = "<xml><id>1</id></xml>";
        serviceMock.stubFor(post(urlEqualTo("/person"))
                .withHeader("Content-Type", equalTo("application/xml"))
                .withRequestBody(equalTo(xmlBody))
                .willReturn(aResponse()
                        .withStatus(200)));

        HttpClient httpClient = HttpClients.createDefault();
        HttpPost httpPost = new HttpPost("http://localhost:8089/person");
        httpPost.setHeader("Content-Type", "application/xml");
        StringEntity xmlEntity = new StringEntity(xmlBody);
        httpPost.setEntity(xmlEntity);

        //when
        HttpResponse response = httpClient.execute(httpPost);

        //then
        assertEquals(response.getStatusLine().getStatusCode(), 200);

    }

    @Test
    public void givenServerThatIsBehindProxy_whenClientIsConfiguredToSendRequestViaProxy_shouldReturn200() throws IOException {
        //given
        proxyMock.stubFor(get(urlMatching(".*"))
                .willReturn(aResponse().proxiedFrom("http://localhost:8089/")));

        serviceMock.stubFor(get(urlEqualTo("/private"))
                .willReturn(aResponse().withStatus(200)));


        HttpHost proxy = new HttpHost("localhost", 8090);
        DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
        HttpClient httpclient = HttpClients.custom()
                .setRoutePlanner(routePlanner)
                .build();

        //when
        final HttpGet httpGet = new HttpGet("http://localhost:8089/private");
        HttpResponse response = httpclient.execute(httpGet);

        //then
        assertEquals(response.getStatusLine().getStatusCode(), 200);
        proxyMock.verify(getRequestedFor(urlEqualTo("/private")));
        serviceMock.verify(getRequestedFor(urlEqualTo("/private")));
    }

    @Test
    public void givenServerThatIsBehindAuthorizationProxy_whenClientSendRequest_shouldAuthorizeProperly() throws IOException {
        //given
        proxyMock.stubFor(get(urlMatching("/private"))
                .willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
        serviceMock.stubFor(get(urlEqualTo("/private"))
                .willReturn(aResponse().withStatus(200)));


        HttpHost proxy = new HttpHost("localhost", 8090);
        DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);

        // Client credentials
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(new AuthScope(proxy),
                new UsernamePasswordCredentials("username_admin", "secret_password"));


        // Create AuthCache instance
        AuthCache authCache = new BasicAuthCache();

        // Generate BASIC scheme object and add it to the local auth cache
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(proxy, basicAuth);
        HttpClientContext context = HttpClientContext.create();
        context.setCredentialsProvider(credentialsProvider);
        context.setAuthCache(authCache);


        HttpClient httpclient = HttpClients.custom()
                .setRoutePlanner(routePlanner)
                .setDefaultCredentialsProvider(credentialsProvider)
                .build();


        //when
        final HttpGet httpGet = new HttpGet("http://localhost:8089/private");
        HttpResponse response = httpclient.execute(httpGet, context);

        //then
        assertEquals(response.getStatusLine().getStatusCode(), 200);
        proxyMock.verify(getRequestedFor(urlEqualTo("/private")).withHeader("Authorization", containing("Basic")));
        serviceMock.verify(getRequestedFor(urlEqualTo("/private")));
    }


}
