package org.zalando.stups.fullstop.plugin.lambda;

import com.amazonaws.services.cloudtrail.processinglibrary.model.CloudTrailEvent;
import com.google.common.collect.ImmutableMap;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.JsonPathException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zalando.stups.fullstop.plugin.AbstractFullstopPlugin;
import org.zalando.stups.fullstop.plugin.lambda.config.LambdaPluginProperties;
import org.zalando.stups.fullstop.violation.ViolationSink;

import java.util.Optional;
import java.util.regex.Pattern;

import static java.util.Optional.empty;
import static org.zalando.stups.fullstop.events.CloudTrailEventSupport.violationFor;
import static org.zalando.stups.fullstop.violation.ViolationType.LAMBDA_FUNCTION_CREATED_FROM_UNTRUSTED_LOCATION;

@Component
public class LambdaPlugin extends AbstractFullstopPlugin {

    private static final String EVENT_SOURCE = "lambda.amazonaws.com";

    private static final Pattern CREATE_FUNCTION_EVENT_REGEXP = Pattern.compile("CreateFunction.*");

    private static final Pattern UPDATE_FUNCTION_EVENT_REGEXP = Pattern.compile("UpdateFunctionCode.*");

    private static final String S3_BUCKET_JSON_PATH = "$.code.s3Bucket";
    private static final String S3_KEY_JSON_PATH = "$.code.s3Key";
    private static final String FUNCTION_NAME_JSON_PATH = "$.functionName";

    private static final String S3_BUCKET = "s3_bucket";
    private static final String S3_KEY = "s3_key";
    private static final String FUNCTION_NAME = "function_name";

    private static final String EMPTY = "";

    private final ViolationSink violationSink;

    private final LambdaPluginProperties lambdaPluginProperties;

    @Autowired
    public LambdaPlugin(final ViolationSink violationSink,
                        final LambdaPluginProperties lambdaPluginProperties) {
        this.violationSink = violationSink;
        this.lambdaPluginProperties = lambdaPluginProperties;
    }

    @Override
    public boolean supports(final CloudTrailEvent cloudTrailEvent) {
        return cloudTrailEvent.getEventData().getEventSource().equals(EVENT_SOURCE)
                && (
                isCreateFunctionEvent(cloudTrailEvent.getEventData().getEventName())
                        || isUpdateFunctionEvent(cloudTrailEvent.getEventData().getEventName())
        );
    }


    @Override
    public void processEvent(final CloudTrailEvent event) {

        final String requestParameters = event.getEventData().getRequestParameters();

        final Optional<String> s3Bucket = getS3Bucket(requestParameters);

        if (!s3Bucket.isPresent() || !lambdaPluginProperties.getS3Buckets().contains(s3Bucket.get())) {
            violationSink.put(
                    violationFor(event)
                            .withPluginFullyQualifiedClassName(LambdaPlugin.class)
                            .withType(LAMBDA_FUNCTION_CREATED_FROM_UNTRUSTED_LOCATION)
                            .withMetaInfo(ImmutableMap
                                    .builder()
                                    .put(FUNCTION_NAME, getFunctionName(requestParameters).orElse(EMPTY))
                                    .put(S3_BUCKET, s3Bucket.orElse(EMPTY))
                                    .put(S3_KEY, getS3BucketKey(requestParameters).orElse(EMPTY))
                                    .build())
                            .build());
        }
    }

    private Optional<String> getS3Bucket(final String parameters) {
        try {
            return Optional.ofNullable(JsonPath.read(parameters, S3_BUCKET_JSON_PATH));
        } catch (final JsonPathException ignored) {
            return empty();
        }
    }

    private Optional<String> getS3BucketKey(final String parameters) {
        try {
            return Optional.ofNullable(JsonPath.read(parameters, S3_KEY_JSON_PATH));
        } catch (final JsonPathException ignored) {
            return empty();
        }
    }

    private Optional<String> getFunctionName(final String parameters) {
        try {
            return Optional.ofNullable(JsonPath.read(parameters, FUNCTION_NAME_JSON_PATH));
        } catch (final JsonPathException ignored) {
            return empty();
        }
    }

    private static boolean isUpdateFunctionEvent(final String eventName) {
        return UPDATE_FUNCTION_EVENT_REGEXP.matcher(eventName).matches();
    }

    private static boolean isCreateFunctionEvent(final String eventName) {
        return CREATE_FUNCTION_EVENT_REGEXP.matcher(eventName).matches();
    }


}

@@ -1257,32 +1257,37 @@
 tring S3_BUCKET_
+CODE_
 JSON_PATH = %22$.c
@@ -1332,24 +1332,164 @@
 l String S3_
+BUCKET_JSON_PATH = %22$.s3Bucket%22;%0A    private static final String S3_KEY_CODE_JSON_PATH = %22$.code.s3Key%22;%0A    private static final String S3_
 KEY_JSON_PAT
@@ -1486,37 +1486,32 @@
 Y_JSON_PATH = %22$
-.code
 .s3Key%22;%0A    pri
@@ -3713,66 +3713,26 @@
     
-try %7B%0A            return Optional.ofNullable(JsonPath.read
+return getFromJSON
 (par
@@ -3742,32 +3742,37 @@
 ters, S3_BUCKET_
+CODE_
 JSON_PATH));%0A   
@@ -3768,60 +3768,106 @@
 PATH
-)
+, S3_BUCKET_JSON_PATH
 );%0A    
+%7D%0A%0A
     
-%7D catch (final JsonPathException ignored
+private Optional%3CString%3E getS3BucketKey(final String parameters
 ) %7B%0A
@@ -3878,130 +3878,467 @@
     
-    
 return 
-empty();%0A        %7D%0A    %7D%0A%0A    private Optional%3CString%3E getS3BucketKey(final String parameters) %7B%0A        try %7B%0A
+getFromJSON(parameters, S3_KEY_CODE_JSON_PATH, S3_KEY_JSON_PATH);%0A    %7D%0A%0A    private Optional%3CString%3E getFromJSON(final String parameters, final String s3BucketCodeJsonPath, final String s3BucketJsonPath) %7B%0A        final Optional%3CString%3E s3bucket;%0A        try %7B%0A            s3bucket = Optional.ofNullable(JsonPath.read(parameters, s3BucketCodeJsonPath));%0A            if (s3bucket.isPresent())%7B%0A                return s3bucket;%0A            %7D else %7B%0A    
     
@@ -4402,27 +4402,41 @@
 rs, 
-S3_KEY_JSON_PATH));
+s3BucketJsonPath));%0A            %7D
 %0A   

add second location in order to find s3bucket and s3key