package com.sourcefellows.bookingfellow.client;

import io.github.resilience4j.bulkhead.BulkheadFullException;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.web.client.RestClient;

import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.*;

@Service
public class Client {

    private final Logger logger = LoggerFactory.getLogger(Client.class);

    public String callHTTPService() {

        logger.info("START calling service");

        RequestConfig requestConfig = RequestConfig.custom()
                .build();

        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        httpClientBuilder.setDefaultRequestConfig(requestConfig);
        httpClientBuilder.disableAutomaticRetries();

        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        httpRequestFactory.setHttpClient(httpClientBuilder.build());

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        RestClient customClient = RestClient.builder()
                .requestFactory(httpRequestFactory)
                .baseUrl("http://localhost:21212")
                .defaultHeader("My-Header", "Foo")
                .build();

        try {
            ResponseEntity<String> response = customClient.get().uri("/trainings").retrieve()
                    .toEntity(String.class);

            return response.getBody();

        } finally {
            stopWatch.stop();
            logger.info("time to run: {} seconds", stopWatch.getTotalTimeSeconds());
        }
    }

    public String callHTTPServiceWithHardTimeout() throws ExecutionException, InterruptedException, TimeoutException {

        logger.info("START calling service with hard timeout");

        final Duration timeout = Duration.ofSeconds(3);
        ExecutorService executor = Executors.newSingleThreadExecutor();

        final Future<String> handler = executor.submit(new Callable() {
            @Override
            public String call() throws Exception {
                return callHTTPService();
            }
        });

        try {
            return handler.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
        } finally {
            logger.info("DONE calling service with hard timeout");
            executor.shutdownNow();
        }
    }

    private static final String BACKEND = "backend";

    @CircuitBreaker(name = BACKEND, fallbackMethod = "fallback")
    public String callWithBulkhead() throws ExecutionException, InterruptedException, TimeoutException {
        return callHTTPService();
    }

    private String fallback(Throwable e) {
        return "empty Bulkhead";
    }
}
