package edu.upc.caminstech.equipstic.client;

import java.net.URI;
import java.util.Arrays;
import java.util.Optional;
import java.util.TimeZone;

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;

public abstract class EquipsTicRestTemplateBuilder {

    public static RestTemplate createRestTemplate(URI baseUri, String username, String password, TimeZone timeZone) {
        HttpClient httpClient = prepareHttpClient(baseUri, username, password);
        return prepareRestTemplate(httpClient, timeZone);
    }

    /**
     * Mètode auxiliar per instanciar un HttpClient a partir de les credencials
     * d'autenticació.
     */
    private static HttpClient prepareHttpClient(URI baseUri, String username, String password) {
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        AuthScope authScope = new AuthScope(baseUri.getHost(), baseUri.getPort());
        Credentials credentials = new UsernamePasswordCredentials(username, password);
        credsProvider.setCredentials(authScope, credentials);
        HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();

        return httpClient;
    }

    private static RestTemplate prepareRestTemplate(HttpClient httpClient, TimeZone timeZone) {
        RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
        fixSupportedMediaTypes(template);
        fixJacksonObjectMapperTimezone(template, timeZone);
        return template;
    }

    /**
     * Reconfigura la serialització/deserialització amb Jackson per tenir en
     * compte implícitament el TimeZone indicat.
     * 
     * Això és necessari perquè Jackson per defecte fa servir el timezone UTC
     * quan no s'explicita el TimeZone, però sembla que el servidor EquipsTIC fa
     * servir CET ("Europe/Madrid" o equivalent).
     */
    private static void fixJacksonObjectMapperTimezone(RestTemplate template, TimeZone timeZone) {
        ObjectMapper mapper = getObjectMapper(template);
        if (mapper != null) {
            mapper.setTimeZone(timeZone);
        }
    }

    private static ObjectMapper getObjectMapper(RestTemplate template) {
        Optional<HttpMessageConverter<?>> converter = template.getMessageConverters().stream()
                .filter(c -> c instanceof MappingJackson2HttpMessageConverter).findFirst();
        if (converter.isPresent()) {
            return ((MappingJackson2HttpMessageConverter) converter.get()).getObjectMapper();
        }
        return null;
    }

    /**
     * Reconfigura els Converters de la RestTemplate per tal que acceptin també
     * respostes de tipus "text/plain".
     *
     * Això és necessari perquè la API retorna erròniament un Content-Type amb
     * valor "text/plain" quan hauria de ser "application/json".
     */
    private static void fixSupportedMediaTypes(RestTemplate template) {
        Optional<HttpMessageConverter<?>> converter = template.getMessageConverters().stream()
                .filter(c -> c instanceof MappingJackson2HttpMessageConverter).findFirst();
        if (converter.isPresent()) {
            MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter.get();
            jsonConverter.setSupportedMediaTypes(Arrays.asList(
                    new MediaType("application", "json", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET),
                    new MediaType("text", "plain", MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)));
        }
    }
}

@@ -855,16 +855,184 @@
 apper;%0A%0A
+/**%0A * Classe per instanciar la %7B@link RestTemplate%7D que utilitza el client.%0A * %3Cp%3E%0A * Aquesta classe %C3%A9s d'%C3%BAs intern de la llibreria, i no %C3%A9s %C3%BAtil per als usuaris.%0A */%0A
 public a
@@ -3023,33 +3023,39 @@
 ) %7B%0A        
-Optional%3C
+MappingJackson2
 HttpMessageC
@@ -3054,36 +3054,32 @@
 MessageConverter
-%3C?%3E%3E
  converter = tem
@@ -3079,110 +3079,18 @@
 r = 
-template.getMessageConverters().stream()%0A                .filter(c -%3E c instanceof Mapping
+get
 Jackson
-2Http
 Mess
@@ -3097,36 +3097,42 @@
 ageConverter
-).findFirst(
+IfPresent(template
 );%0A        i
@@ -3139,36 +3139,32 @@
 f (converter
-.isPresent()
+ != null
 ) %7B%0A        
@@ -3178,63 +3178,17 @@
 urn 
-((MappingJackson2HttpMessageConverter) converter.get())
+converter
 .get
@@ -3612,17 +3612,23 @@
     
-Optional%3C
+MappingJackson2
 Http
@@ -3647,12 +3647,8 @@
 rter
-%3C?%3E%3E
  con
@@ -3660,20 +3660,18 @@
 r = 
-template.get
+getJackson
 Mess
@@ -3686,560 +3686,801 @@
 rter
-s().stream()%0A                .filter(c -%3E c instanceof MappingJackson2HttpMessageConverter).findFirst();%0A        if (converter.isPresent()) %7B%0A            MappingJackson2HttpMessageConverter jsonConverter = (MappingJackson2HttpMessageConverter) converter.get();%0A            jsonConverter.setSupportedMediaTypes(Arrays.asList(%0A                    new MediaType(%22application%22, %22json%22, MappingJackson2HttpMessageConverter.DEFAULT_CHARSET),%0A                    new MediaType(%22text%22, %22plain%22, MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)));%0A        %7D
+IfPresent(template);%0A        if (converter != null) %7B%0A            converter.setSupportedMediaTypes(Arrays.asList(%0A                    new MediaType(%22application%22, %22json%22, MappingJackson2HttpMessageConverter.DEFAULT_CHARSET),%0A                    new MediaType(%22text%22, %22plain%22, MappingJackson2HttpMessageConverter.DEFAULT_CHARSET)));%0A        %7D%0A    %7D%0A%0A    private static MappingJackson2HttpMessageConverter getJacksonMessageConverterIfPresent(RestTemplate template) %7B%0A        Optional%3CHttpMessageConverter%3C?%3E%3E converter = template.getMessageConverters().stream()%0A                .filter(c -%3E c instanceof MappingJackson2HttpMessageConverter).findFirst();%0A        if (converter.isPresent()) %7B%0A            return (MappingJackson2HttpMessageConverter) converter.get();%0A        %7D%0A        return null;
 %0A   

refactor. extract method