package org.embulk.input.http;

import com.google.common.collect.ImmutableList;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.util.EntityUtils;
import org.embulk.spi.Exec;
import org.embulk.spi.util.RetryExecutor;
import org.slf4j.Logger;

import javax.net.ssl.SSLException;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.util.List;

public class RetryableHandler implements RetryExecutor.Retryable
{
    protected final Logger logger = Exec.getLogger(getClass());

    private static final List<Class<? extends IOException>> NOT_RETRIABLE_CLASSES = ImmutableList.of(UnknownHostException.class,
            InterruptedIOException.class, SSLException.class);

    private final HttpClient client;
    private final HttpRequestBase request;
    private HttpResponse response;

    public RetryableHandler(HttpClient client, HttpRequestBase request)
    {
        this.client = client;
        this.request = request;
    }

    public HttpResponse getResponse()
    {
        return response;
    }

    @Override
    public Object call() throws Exception
    {
        if (response != null) {
            throw new IllegalStateException("response is already set");
        }
        HttpResponse response = client.execute(request);
        statusIsOkOrThrow(response);
        this.response = response;
        return null;
    }

    @Override
    public boolean isRetryableException(Exception exception)
    {
        if (NOT_RETRIABLE_CLASSES.contains(exception.getClass())) {
            logger.error(String.format("'%s' is not retriable", exception.getClass()));
            return false;
        }
        return true;
    }

    @Override
    public void onRetry(Exception exception, int retryCount, int retryLimit, int retryWait)
            throws RetryExecutor.RetryGiveupException
    {
        logger.warn("retrying {}/{} after {} seconds. Message: {}",
                retryCount, retryLimit, retryWait / 1000,
                exception.getMessage());
    }

    @Override
    public void onGiveup(Exception firstException, Exception lastException)
            throws RetryExecutor.RetryGiveupException
    {
        logger.error("giveup {}", lastException.getMessage());
    }

    protected void statusIsOkOrThrow(HttpResponse response)
            throws HttpException, IOException
    {
        int code = response.getStatusLine().getStatusCode();
        switch (response.getStatusLine().getStatusCode()) {
            case 200:
                return;
            default:
                throw new HttpException(String.format("Request is not successful, code=%d, body=%s",
                        code, EntityUtils.toString(response.getEntity())));
        }
    }
}
