package org.wso2.carbon.apimgt.impl.loader;

import com.google.gson.Gson;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
import org.wso2.carbon.apimgt.api.APIManagementException;
import org.wso2.carbon.apimgt.api.model.KeyManagerConfiguration;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.APIManagerConfiguration;
import org.wso2.carbon.apimgt.impl.dto.EventHubConfigurationDto;
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
import org.wso2.carbon.apimgt.impl.utils.APIUtil;

import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Timer;
import java.util.TimerTask;

public class KeyManagerConfigurationDataRetriever extends TimerTask {

    public KeyManagerConfigurationDataRetriever(String tenantDomain) {

        this.tenantDomain = tenantDomain;
    }

    private Log log = LogFactory.getLog(KeyManagerConfigurationDataRetriever.class);
    private String tenantDomain;
    @Override
    public void run() {

        APIManagerConfiguration apiManagerConfiguration =
                ServiceReferenceHolder.getInstance().getAPIManagerConfigurationService()
                        .getAPIManagerConfiguration();
        if (apiManagerConfiguration != null) {
            EventHubConfigurationDto eventHubConfigurationDto =
                    apiManagerConfiguration.getEventHubConfigurationDto();
            if (eventHubConfigurationDto != null && eventHubConfigurationDto.isEnabled()) {
                try {
                    String url = eventHubConfigurationDto.getServiceUrl().concat(APIConstants.INTERNAL_WEB_APP_EP)
                            .concat("/keymanagers");
                    byte[] credentials = Base64.encodeBase64((eventHubConfigurationDto.getUsername() + ":" +
                            eventHubConfigurationDto.getPassword()).getBytes());
                    HttpGet method = new HttpGet(url);
                    method.setHeader("Authorization", "Basic " + new String(credentials, StandardCharsets.UTF_8));
                    method.setHeader(APIConstants.HEADER_TENANT, tenantDomain);
                    URL configUrl = new URL(url);
                    int port = configUrl.getPort();
                    String protocol = configUrl.getProtocol();
                    HttpClient httpClient = APIUtil.getHttpClient(port, protocol);
                    HttpResponse httpResponse = null;
                    int retryCount = 0;
                    boolean retry;
                    do {
                        try {
                            httpResponse = httpClient.execute(method);
                            if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                                String responseString = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
                                KeyManagerConfiguration[] keyManagerConfigurations =
                                        new Gson().fromJson(responseString, KeyManagerConfiguration[].class);
                                for (KeyManagerConfiguration keyManagerConfiguration : keyManagerConfigurations) {
                                    if (keyManagerConfiguration.isEnabled()) {
                                        try {
                                            ServiceReferenceHolder.getInstance().getKeyManagerConfigurationService()
                                                    .addKeyManagerConfiguration(
                                                            keyManagerConfiguration.getTenantDomain(),
                                                            keyManagerConfiguration.getName(),
                                                            keyManagerConfiguration.getType(),
                                                            keyManagerConfiguration);
                                        } catch (APIManagementException e) {
                                            log.error("Error while configuring Key Manager " +
                                                    keyManagerConfiguration.getName() +
                                                    " in tenant " + keyManagerConfiguration.getTenantDomain(), e);
                                        }
                                    }
                                }
                                retry = false;
                            } else {
                                retry = true;
                                retryCount++;
                            }
                        } catch (IOException ex) {
                            retryCount++;
                            int maxRetries = 15;
                            if (retryCount < maxRetries) {
                                retry = true;
                                long retryTimeout = (long) Math.min(Math.pow(2, retryCount), 300);
                                log.warn("Failed retrieving Key Manager Configurations from remote " +
                                        "endpoint: " + ex.getMessage()
                                        + ". Retrying after " + retryTimeout + " seconds...");
                                Thread.sleep(retryTimeout * 1000);
                            } else {
                                throw ex;
                            }
                        }
                    } while (retry);
                } catch (InterruptedException | IOException  e) {
                    log.error("Error while retrieving key manager configurations", e);
                }
            }
        }
    }

    public void startLoadKeyManagerConfigurations() {
        new Timer().schedule(this, ((int) (Math.random() * 60) + 900) * 1000);
    }
}

@@ -2797,16 +2797,57 @@
  retry;%0A
+                    int maxRetries = 15;%0A
         
@@ -4955,32 +4955,36 @@
                 
+if (
 retryCount++;%0A  
@@ -4969,36 +4969,50 @@
   if (retryCount
-++;%0A
+ %3C maxRetries) %7B%0A 
                 
@@ -5023,36 +5023,204 @@
         
-int maxRetries = 15;
+   retry = true;%0A                            %7D else %7B%0A                                throw ex;%0A                            %7D%0A                        %7D%0A                        if (retry) %7B
 %0A       
@@ -5300,39 +5300,39 @@
            retry
- = true
+Count++
 ;%0A              
@@ -5572,73 +5572,8 @@
 oint
-: %22 + ex.getMessage()%0A                                        + %22
 . Re
@@ -5752,32 +5752,112 @@
             
-throw ex
+log.error(%22Failed to retrieve Key Manager configuration with %22 + retryCount + %22 times.%22)
 ;%0A          
@@ -6234,49 +6234,9 @@
 is, 
-((int) (Math.random() * 60) + 900) * 1000
+5
 );%0A 

Refactor km retrieval