/*
Copyright 2015-2019 KR Abram

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.eclecticlogic.stepper.install;

import com.eclecticlogic.stepper.StateMachine;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.Runtime;
import software.amazon.awssdk.services.lambda.model.*;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class LambdaInstaller {

    private final static String LAMBDA_NAME_SUFFIX = "_stepperLambda";

    private final StateMachine machine;
    private final InstallConfig config;
    private final LambdaClient lambdaClient;


    public LambdaInstaller(StateMachine machine, InstallConfig config) {
        this.machine = machine;
        this.config = config;
        lambdaClient = LambdaClient.builder().credentialsProvider(DefaultCredentialsProvider.create()).build();
    }


    public String getLambdaName() {
        return machine.getName() + LAMBDA_NAME_SUFFIX;
    }


    boolean isLambdaExists() {
        try {
            lambdaClient.getFunction(b -> b.functionName(getLambdaName()));
            return true;
        } catch (ResourceConflictException e) {
            return false;
        }
    }


    /**
     * TODO: Check if we can update instead.
     */
    void deleteLambda() {
        lambdaClient.deleteFunction(b -> b.functionName(getLambdaName()));
    }


    FunctionCode createZippedLambdaCode() {
        Path zipFile;
        try {
            zipFile = Files.createTempFile("stepper", ".zip");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile.toFile()))) {
            ZipEntry e = new ZipEntry("index.js");
            out.putNextEntry(e);

            byte[] data = machine.getLambda().getBytes();
            out.write(data, 0, data.length);
            out.closeEntry();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            return FunctionCode.builder().zipFile(SdkBytes.fromByteArray(Files.readAllBytes(zipFile))).build();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    public String install() {
        if (isLambdaExists()) {
            deleteLambda();
        }

        CreateFunctionRequest.Builder lambdaBuilder = CreateFunctionRequest.builder()
                .functionName(machine.getName() + LAMBDA_NAME_SUFFIX)
                .code(createZippedLambdaCode())
                .handler("index.handler")
                .runtime(Runtime.NODEJS10_X)
                .role(config.getLambda().getExecutionRole());
        CreateFunctionResponse response = lambdaClient.createFunction(lambdaBuilder.build());
        return response.functionArn();
    }
}

@@ -1717,30 +1717,103 @@
     
-boolean isL
+/**%0A     * @return arn if l
 ambda
-E
+ e
 xists
+, otherwise null%0A     */%0A    String getExistingLambdaArn
 () %7B
@@ -1831,32 +1831,39 @@
 y %7B%0A            
+return 
 lambdaClient.get
@@ -1912,33 +1912,38 @@
 ()))
-;%0A            return true
+.configuration().functionArn()
 ;%0A  
@@ -2011,13 +2011,12 @@
 urn 
-false
+null
 ;%0A  
@@ -2039,90 +2039,168 @@
     
-/**%0A     * TODO: Check if we can update instead.%0A     */%0A    void deleteLambda() %7B
+void updateLambda(String arn) %7B%0A        lambdaClient.updateFunctionCode(b -%3E b.functionName(getLambdaName())%0A                .zipFile(createSdkBytesForCode()));
 %0A   
@@ -2217,20 +2217,20 @@
 aClient.
-dele
+upda
 teFuncti
@@ -2223,32 +2223,45 @@
 t.updateFunction
+Configuration
 (b -%3E b.function
@@ -2285,54 +2285,110 @@
 e())
-)
-;
 %0A    
-%7D%0A%0A%0A    FunctionCode createZippedLambda
+            .role(config.getLambda().getExecutionRole()));%0A    %7D%0A%0A%0A    SdkBytes createSdkBytesFor
 Code
@@ -3018,39 +3018,8 @@
 urn 
-FunctionCode.builder().zipFile(
 SdkB
@@ -3065,25 +3065,16 @@
 ipFile))
-).build()
 ;%0A      
@@ -3170,62 +3170,235 @@
     
-public String install() %7B%0A        if (isLambdaExists()
+FunctionCode createZippedLambdaCode() %7B%0A        return FunctionCode.builder().zipFile(createSdkBytesForCode()).build();%0A%0A    %7D%0A%0A%0A    public String install() %7B%0A        String arn = getExistingLambdaArn();%0A        if (arn != null
 ) %7B%0A
@@ -3413,12 +3413,12 @@
     
-dele
+upda
 teLa
@@ -3422,16 +3422,19 @@
 eLambda(
+arn
 );%0A     
@@ -3437,26 +3437,34 @@
        %7D
-%0A%0A
+ else %7B%0A  
         
 CreateFu
@@ -3447,32 +3447,34 @@
 lse %7B%0A          
+  
 CreateFunctionRe
@@ -3535,16 +3535,20 @@
 ilder()%0A
+    
         
@@ -3625,16 +3625,20 @@
         
+    
 .code(cr
@@ -3677,16 +3677,20 @@
         
+  
+  
 .handler
@@ -3707,16 +3707,20 @@
 ndler%22)%0A
+    
         
@@ -3764,32 +3764,36 @@
                 
+    
 .role(config.get
@@ -3822,32 +3822,36 @@
 ole());%0A        
+ 
+   
 CreateFunctionRe
@@ -3928,22 +3928,25 @@
         
-return
+    arn =
  respons
@@ -3958,20 +3958,51 @@
 ctionArn();%0A
+        %7D%0A%0A        return arn;%0A
     %7D%0A%7D%0A

update instead of delete for lambda helper.