package go.kr.project.api.config;

import com.google.common.util.concurrent.RateLimiter;
import go.kr.project.api.config.properties.VmisProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;

/**
 * RestTemplate 설정 클래스
 * 외부 VMIS-interface API 호출용 설정
 * Apache HttpClient 4 기반 연결 풀 관리 및 타임아웃 설정
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
public class RestTemplateConfig {

    private final VmisProperties vmisProperties;

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        VmisProperties.RestTemplateProps config = vmisProperties.getRestTemplate();

        log.info("RestTemplate 설정 - 외부 VMIS-interface API 호출용");

        // 타임아웃 설정
        int connectTimeout = config.getTimeout().getConnectTimeoutMillis();
        int readTimeout = config.getTimeout().getReadTimeoutMillis();

        log.info("  - 타임아웃: connectTimeout={}ms, readTimeout={}ms",
                connectTimeout, readTimeout);

        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(connectTimeout)
                .setSocketTimeout(readTimeout)
                .setConnectionRequestTimeout(connectTimeout)
                .build();

        // 연결 풀 관리 설정
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(config.getConnectionPool().getMaxTotal());
        cm.setDefaultMaxPerRoute(config.getConnectionPool().getMaxPerRoute());

        log.info("  - 연결 풀: maxTotal={}, maxPerRoute={}",
                config.getConnectionPool().getMaxTotal(),
                config.getConnectionPool().getMaxPerRoute());

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

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);

        // RestTemplate 생성
        RestTemplate restTemplate = builder
                .requestFactory(() -> requestFactory)
                .build();

        // Rate Limiter 인터셉터 추가 (0 이하면 제한 없음)
        double permitsPerSecond = config.getRateLimit().getPermitsPerSecond();
        if (permitsPerSecond > 0) {
            log.info("  - Rate Limiter: 활성화 (초당 {} 건으로 제한)", permitsPerSecond);
            restTemplate.getInterceptors().add(new RateLimitInterceptor(permitsPerSecond));
        } else {
            log.info("  - Rate Limiter: 비활성화 (제한 없음)");
        }

        return restTemplate;
    }

    /**
     * Rate Limiting 인터셉터
     * Guava의 RateLimiter를 사용하여 요청 속도 제한
     */
    @SuppressWarnings("UnstableApiUsage")
    private static class RateLimitInterceptor implements ClientHttpRequestInterceptor {
        private final RateLimiter rateLimiter;
        private final double permitsPerSecond;

        public RateLimitInterceptor(double permitsPerSecond) {
            this.permitsPerSecond = permitsPerSecond;
            this.rateLimiter = RateLimiter.create(permitsPerSecond);
            log.debug("RateLimitInterceptor 생성 - 초당 {} 건", permitsPerSecond);
        }

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                            ClientHttpRequestExecution execution) throws IOException {
            // 요청 전에 Rate Limiter를 통해 대기
            double waitTime = rateLimiter.acquire();
            if (waitTime > 0) {
                log.debug("Rate Limiter 대기 - {}초 대기 후 요청: {} {}",
                        String.format("%.3f", waitTime),
                        request.getMethod(),
                        request.getURI());
            }
            return execution.execute(request, body);
        }
    }
}
