package kr.xit.core.spring.config.support;

import java.io.*;
import java.net.*;
import java.util.*;

import org.apache.http.client.config.*;
import org.apache.http.config.*;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.annotation.*;
import org.springframework.http.*;
import org.springframework.http.client.*;
import org.springframework.web.client.*;

import com.fasterxml.jackson.databind.*;

import kr.xit.core.model.*;
import kr.xit.core.support.utils.*;
import lombok.extern.slf4j.*;

@Slf4j
@Configuration
public class RestTemplateConfig {

    @Value("${app.restTemplate.socketTimeout:5000}")
    private int socketTimeout;

    @Value("${app.restTemplate.connectionTimeout:2000}")
    private int connectionTimeout;

    @Value("${app.restTemplate.requestTimeout:2000}")
    private int requestTimeout;

    @Value("${app.restTemplate.pool.max:10}")
    private int poolMax;

    @Value("${app.restTemplate.pool.max-per-route:5}")
    private int maxPerRoute;

    // FIXME:: RetryTemplate 미사용
    // @Value("${app.restTemplate.retry.delay:200}")
    // private int retryDelay;
    //
    // @Value("${app.restTemplate.retry.max-delay:5}")
    // private int retryMaxDelay;
    //
    // @Value("${app.restTemplate.retry.max-attempts:3}")
    // private int maxAttempts;
    
    @Bean
    public RestTemplate restTemplate() {
        ConnectionConfig connectionConfig = ConnectionConfig.custom()
            .build();

        RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(socketTimeout)
            .setConnectTimeout(connectionTimeout)
            .setConnectionRequestTimeout(requestTimeout)
            .build();

        PoolingHttpClientConnectionManager connectionPool = new PoolingHttpClientConnectionManager();
        connectionPool.setMaxTotal(poolMax);
        connectionPool.setDefaultMaxPerRoute(maxPerRoute);
        connectionPool.setDefaultConnectionConfig(connectionConfig);

        CloseableHttpClient httpClient = HttpClientBuilder.create()
            .setConnectionManager(connectionPool)
            .setDefaultRequestConfig(requestConfig)
            .evictExpiredConnections()
            .build();

        RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
        restTemplate.setErrorHandler(new CustomResponseErrorHandler());
        //restTemplate.getInterceptors().add(requestInterceptor);

        return restTemplate;
    }

    // FIXME:: RetryTemplate 미사용
    // @Bean
    // public ClientHttpRequestInterceptor requestInterceptor(final RetryTemplate retryTemplate) {
    //     return (request, body, execution) ->
    //         retryTemplate.execute(context -> execution.execute(request, body));
    // }

    // @Bean
    // public RetryTemplate retryTemplate() {
    //     BackOffPolicy backOffPolicy = BackOffPolicyBuilder.newBuilder()
    //         .delay(retryDelay)
    //         .maxDelay(retryMaxDelay)
    //         .build();
    //
    //     RetryTemplate retryTemplate = new RetryTemplate();
    //     retryTemplate.setRetryPolicy(new SimpleRetryPolicy(maxAttempts));
    //     retryTemplate.setBackOffPolicy(backOffPolicy);
    //
    //     return retryTemplate;
    // }
    
    class CustomResponseErrorHandler  implements ResponseErrorHandler {
        ObjectMapper objectMapper = new ObjectMapper();
        public CustomResponseErrorHandler() {
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
        
        @Override
        public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
            HttpStatus status = clientHttpResponse.getStatusCode();
            return status.is4xxClientError() || status.is5xxServerError();
        }

        @Override
        public void handleError(ClientHttpResponse response) throws IOException {
            String responseAsXmlString = toString(response.getBody());
            String responseAsString = JsonUtils.jsonFromXml(responseAsXmlString, ApiResponseDTO.class);
            log.error("ResponseBody: {}", responseAsString);

            throw new CustomException(responseAsString);
        }

        @Override
        public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
            String responseAsXmlString = toString(response.getBody());
            String responseAsString = JsonUtils.jsonFromXml(responseAsXmlString, ApiResponseDTO.class);
            log.error("URL: {}, HttpMethod: {}, ResponseBody: {}", url, method, responseAsString);

            throw new CustomException(responseAsString);
        }

        String toString(InputStream inputStream) {
            Scanner s = new Scanner(inputStream).useDelimiter("\\A");
            return s.hasNext() ? s.next() : "";
        }

        static class CustomException extends IOException {
            public CustomException(String message) {
                super(message);
            }
        }
    }
}