/*
 * Copyright 2017 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.netflix.kayenta.aws.config;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.S3ClientOptions;
import com.netflix.kayenta.aws.security.AwsCredentials;
import com.netflix.kayenta.aws.security.AwsNamedAccountCredentials;
import com.netflix.kayenta.security.AccountCredentials;
import com.netflix.kayenta.security.AccountCredentialsRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.List;
import java.util.Optional;

@Configuration
@EnableConfigurationProperties
@ConditionalOnProperty("kayenta.aws.enabled")
@ComponentScan({"com.netflix.kayenta.aws"})
@Slf4j
public class AwsConfiguration {

  @Bean
  @ConfigurationProperties("kayenta.aws")
  AwsConfigurationProperties awsConfigurationProperties() {
    return new AwsConfigurationProperties();
  }

  @Bean
  boolean registerAwsCredentials(AwsConfigurationProperties awsConfigurationProperties,
                                 AccountCredentialsRepository accountCredentialsRepository) throws IOException {
    for (AwsManagedAccount awsManagedAccount : awsConfigurationProperties.getAccounts()) {
      String name = awsManagedAccount.getName();
      List<AccountCredentials.Type> supportedTypes = awsManagedAccount.getSupportedTypes();

      log.info("Registering AWS account {} with supported types {}.", name, supportedTypes);

      ClientConfiguration clientConfiguration = new ClientConfiguration();

      if (awsManagedAccount.getProxyProtocol() != null) {
        if (awsManagedAccount.getProxyProtocol().equalsIgnoreCase("HTTPS")) {
          clientConfiguration.setProtocol(Protocol.HTTPS);
        } else {
          clientConfiguration.setProtocol(Protocol.HTTP);
        }
        Optional.ofNullable(awsManagedAccount.getProxyHost())
          .ifPresent(clientConfiguration::setProxyHost);
        Optional.ofNullable(awsManagedAccount.getProxyPort())
          .map(Integer::parseInt)
          .ifPresent(clientConfiguration::setProxyPort);
      }

      String profileName = awsManagedAccount.getProfileName();
      AmazonS3 amazonS3;

      if (StringUtils.isEmpty(profileName)) {
        amazonS3 = AmazonS3ClientBuilder.defaultClient();
      } else {
        amazonS3 = AmazonS3ClientBuilder.standard().withCredentials(new ProfileCredentialsProvider(profileName)).build();
      }

      if (awsManagedAccount.getEndpoint() != null) {
        amazonS3.setEndpoint(awsManagedAccount.getEndpoint());
        amazonS3.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
      } else {
        Optional.ofNullable(awsManagedAccount.getRegion())
          .map(Regions::fromName)
          .map(Region::getRegion)
          .ifPresent(amazonS3::setRegion);
      }

      try {
        AwsCredentials awsCredentials = new AwsCredentials();
        AwsNamedAccountCredentials.AwsNamedAccountCredentialsBuilder awsNamedAccountCredentialsBuilder =
          AwsNamedAccountCredentials
            .builder()
            .name(name)
            .credentials(awsCredentials);

        if (!CollectionUtils.isEmpty(supportedTypes)) {
          if (supportedTypes.contains(AccountCredentials.Type.OBJECT_STORE)) {
            String bucket = awsManagedAccount.getBucket();
            String rootFolder = awsManagedAccount.getRootFolder();

            if (StringUtils.isEmpty(bucket)) {
              throw new IllegalArgumentException("AWS/S3 account " + name + " is required to specify a bucket.");
            }

            if (StringUtils.isEmpty(rootFolder)) {
              throw new IllegalArgumentException("AWS/S3 account " + name + " is required to specify a rootFolder.");
            }

            awsNamedAccountCredentialsBuilder.bucket(bucket);
            awsNamedAccountCredentialsBuilder.region(awsManagedAccount.getRegion());
            awsNamedAccountCredentialsBuilder.rootFolder(rootFolder);
            awsNamedAccountCredentialsBuilder.amazonS3(amazonS3);
          }

          awsNamedAccountCredentialsBuilder.supportedTypes(supportedTypes);
        }

        AwsNamedAccountCredentials awsNamedAccountCredentials = awsNamedAccountCredentialsBuilder.build();
        accountCredentialsRepository.save(name, awsNamedAccountCredentials);
      } catch (Throwable t) {
        log.error("Could not load AWS account " + name + ".", t);
      }
    }

    return true;
  }
}

@@ -789,60 +789,39 @@
 aws.
-regions.Region;%0Aimport com.amazonaws.regions.Regions
+client.builder.AwsClientBuilder
 ;%0Aim
@@ -921,58 +921,8 @@
 er;%0A
-import com.amazonaws.services.s3.S3ClientOptions;%0A
 impo
@@ -3287,24 +3287,110 @@
 );%0A      %7D%0A%0A
+      AmazonS3ClientBuilder amazonS3ClientBuilder = AmazonS3ClientBuilder.standard();%0A
       String
@@ -3443,33 +3443,8 @@
 e();
-%0A      AmazonS3 amazonS3;
 %0A%0A  
@@ -3443,32 +3443,33 @@
 e();%0A%0A      if (
+!
 StringUtils.isEm
@@ -3500,35 +3500,24 @@
     amazonS3
- = AmazonS3
 ClientBuilde
@@ -3522,92 +3522,8 @@
 der.
-defaultClient();%0A      %7D else %7B%0A        amazonS3 = AmazonS3ClientBuilder.standard().
 with
@@ -3578,24 +3578,16 @@
 leName))
-.build()
 ;%0A      
@@ -3587,36 +3587,50 @@
 %0A      %7D%0A%0A      
-if (
+String endpoint = 
 awsManagedAccoun
@@ -3648,79 +3648,167 @@
 nt()
- != null) %7B%0A        amazonS3.setEndpoint(awsManagedAccount.getE
+;%0A%0A      if (!StringUtils.isEmpty(endpoint)) %7B%0A        amazonS3ClientBuilder.setEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(e
 ndpoint
-(
+, null
 ));%0A
@@ -3827,53 +3827,21 @@
 onS3
-.setS3ClientOptions(S3ClientOptions.b
+ClientB
 uilder
-()
 .set
@@ -3859,22 +3859,20 @@
 cess
-(true).build()
+Enabled(true
 );%0A 
@@ -3893,32 +3893,43 @@
         Optional
+%0A          
 .ofNullable(awsM
@@ -3970,55 +3970,42 @@
    .
-map(Regions::fromName)%0A          .map(Region::g
+ifPresent(amazonS3ClientBuilder::s
 etRe
@@ -4013,59 +4013,75 @@
 ion)
+;
 %0A      
+%7D%0A%0A
     
-.ifPresent(amazonS3::setRegion);%0A      %7D
+  AmazonS3 amazonS3 = amazonS3ClientBuilder.build();
 %0A%0A  

Set region and other attributes before invoking build() on builder. (#341)