package incheon.com.config;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import lombok.extern.slf4j.Slf4j;

@Configuration
@Slf4j
public class RestTemplateConfig {
    @Bean
	HttpClient httpClient(@Value("${ssl.connection.allowed-hosts}") List<String> allowedHosts)
			throws GeneralSecurityException {

        // 모든 인증서(사설 인증서 / 공공 인증서)를 신뢰하는 SSLContext 생성
		SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(TrustAllStrategy.INSTANCE).build();
		
		// 허용할 TLS 버전 전부 오픈
		String[] enabledProtocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" };

		SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, enabledProtocols, null,
				(hostname, session) -> {
					// 특정 IP(host)만 허용
					return allowedHosts.contains(hostname.toLowerCase())
							|| HttpsURLConnection.getDefaultHostnameVerifier().verify(hostname, session);
				});
    	
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(30, TimeUnit.SECONDS);
        cm.setMaxTotal(1000);
        cm.setDefaultMaxPerRoute(20);

        HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(2, true);

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5_000)
                .setConnectionRequestTimeout(2_000)
                .setSocketTimeout(30_000)
                .build();
        CookieStore cookieStore = new BasicCookieStore();

        CloseableHttpClient httpClient = HttpClients.custom()
        			.setSSLSocketFactory(socketFactory)
        			.setDefaultCookieStore(cookieStore)
                .setConnectionManager(cm)
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(retryHandler)
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                .evictExpiredConnections()
                .evictIdleConnections(30, TimeUnit.SECONDS)
                .build();
        return httpClient;
	}

    @Bean
    RestTemplate restTemplate(HttpClient httpClient) throws GeneralSecurityException {
    	

        HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);

        // 대용량 업로드 시 버퍼링 끄기(스트리밍)
        // factory.setBufferRequestBody(false);

        RestTemplate rt = new RestTemplate(factory);

        // 메시지 컨버터 UTF-8 등 필요 시 커스터마이즈
        // rt.setMessageConverters(List.of(new MappingJackson2HttpMessageConverter(), new StringHttpMessageConverter(StandardCharsets.UTF_8)));

        return rt;
    }
}
