<commit_before>package com.mcdaniel.serverless;


import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;

import com.amazonaws.event.ProgressEvent;
import com.amazonaws.event.ProgressEventType;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.apigateway.AmazonApiGateway;
import com.amazonaws.services.apigateway.AmazonApiGatewayClientBuilder;
import com.amazonaws.services.apigateway.model.ContentHandlingStrategy;
import com.amazonaws.services.apigateway.model.CreateResourceRequest;
import com.amazonaws.services.apigateway.model.CreateResourceResult;
import com.amazonaws.services.apigateway.model.DeleteResourceRequest;
import com.amazonaws.services.apigateway.model.DeleteResourceResult;
import com.amazonaws.services.apigateway.model.GetResourcesRequest;
import com.amazonaws.services.apigateway.model.GetResourcesResult;
import com.amazonaws.services.apigateway.model.GetRestApisRequest;
import com.amazonaws.services.apigateway.model.GetRestApisResult;
import com.amazonaws.services.apigateway.model.Integration;
import com.amazonaws.services.apigateway.model.IntegrationResponse;
import com.amazonaws.services.apigateway.model.IntegrationType;
import com.amazonaws.services.apigateway.model.Method;
import com.amazonaws.services.apigateway.model.PutIntegrationRequest;
import com.amazonaws.services.apigateway.model.PutIntegrationResult;
import com.amazonaws.services.apigateway.model.PutMethodRequest;
import com.amazonaws.services.apigateway.model.PutMethodResponseRequest;
import com.amazonaws.services.apigateway.model.PutMethodResponseResult;
import com.amazonaws.services.apigateway.model.PutMethodResult;
import com.amazonaws.services.apigateway.model.Resource;
import com.amazonaws.services.apigateway.model.RestApi;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClientBuilder;
import com.amazonaws.services.identitymanagement.model.AttachRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.AttachRolePolicyResult;
import com.amazonaws.services.identitymanagement.model.CreatePolicyRequest;
import com.amazonaws.services.identitymanagement.model.CreatePolicyResult;
import com.amazonaws.services.identitymanagement.model.CreateRoleRequest;
import com.amazonaws.services.identitymanagement.model.CreateRoleResult;
import com.amazonaws.services.identitymanagement.model.DeletePolicyRequest;
import com.amazonaws.services.identitymanagement.model.DeletePolicyResult;
import com.amazonaws.services.identitymanagement.model.DetachRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.DetachRolePolicyResult;
import com.amazonaws.services.identitymanagement.model.GetRoleRequest;
import com.amazonaws.services.identitymanagement.model.GetRoleResult;
import com.amazonaws.services.identitymanagement.model.GetUserResult;
import com.amazonaws.services.identitymanagement.model.NoSuchEntityException;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.AddPermissionRequest;
import com.amazonaws.services.lambda.model.AddPermissionResult;
import com.amazonaws.services.lambda.model.CreateAliasRequest;
import com.amazonaws.services.lambda.model.CreateAliasResult;
import com.amazonaws.services.lambda.model.CreateFunctionRequest;
import com.amazonaws.services.lambda.model.CreateFunctionResult;
import com.amazonaws.services.lambda.model.DeleteFunctionRequest;
import com.amazonaws.services.lambda.model.DeleteFunctionResult;
import com.amazonaws.services.lambda.model.Environment;
import com.amazonaws.services.lambda.model.FunctionCode;
import com.amazonaws.services.lambda.model.FunctionConfiguration;
import com.amazonaws.services.lambda.model.GetPolicyRequest;
import com.amazonaws.services.lambda.model.GetPolicyResult;
import com.amazonaws.services.lambda.model.ListFunctionsResult;
import com.amazonaws.services.lambda.model.PublishVersionRequest;
import com.amazonaws.services.lambda.model.PublishVersionResult;
import com.amazonaws.services.lambda.model.RemovePermissionRequest;
import com.amazonaws.services.lambda.model.RemovePermissionResult;
import com.amazonaws.services.lambda.model.UpdateAliasRequest;
import com.amazonaws.services.lambda.model.UpdateAliasResult;
import com.amazonaws.services.lambda.model.UpdateFunctionCodeRequest;
import com.amazonaws.services.lambda.model.UpdateFunctionCodeResult;
import com.amazonaws.services.lambda.model.UpdateFunctionConfigurationRequest;
import com.amazonaws.services.lambda.model.UpdateFunctionConfigurationResult;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.mcdaniel.serverless.policy.LambdaPolicy;
import com.mcdaniel.serverless.policy.Statement;

import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import io.swagger.parser.SwaggerParser;
 
/**
 * Says "Hi" to the user.
 *
 */
@Mojo( name = "deploy", defaultPhase=LifecyclePhase.DEPLOY, requiresOnline=true, requiresProject=true)
public class ServerlessDeployMojo extends BaseServerlessMojo
{
	
	private static final String DEFAULT_PRODUCES_CONTENT_TYPE = "application/json";
    private static final String EXTENSION_AUTH = "x-amazon-apigateway-auth";
    private static final String EXTENSION_INTEGRATION = "x-amazon-apigateway-integration";
    
    private Swagger swagger;
    
	public void execute() throws MojoExecutionException
    {
    	String rolePolicy = null;

    	getLog().info("serverless-maven-plugin:deploy starting");
        getLog().info("Logger class: " + getLog().getClass().getName());
        
        if ( project != null )
        {
        	getLog().info("Name/Packaging: " + project.getName() + " / " + project.getPackaging());
        	getLog().info("Artifact name: " + project.getArtifact().getFile().getName());
        }
        
        // Create the credentials for all of the clients.
//        getLog().info("AccessKey: " + AWSAccessKey);
//        BasicAWSCredentials awsCredentials = new BasicAWSCredentials(AWSAccessKey, AWSSecretKey);
        
        // Setup needed variables
        String assumedRoleName = serviceName + "_AssumedRole";
        
        // Create all of the needed clients.
        getLog().info("Getting clients...");
//        AWSLambdaClient lambdaClient = new AWSLambdaClient(awsCredentials);
//        AmazonS3Client s3Client = new AmazonS3Client(awsCredentials);
//        AmazonIdentityManagementClient iamClient = new AmazonIdentityManagementClient(awsCredentials);
//        AmazonApiGatewayClient apiClient = new AmazonApiGatewayClient(awsCredentials);
        

        getLog().info("Done.");
        
        String accountNumber = null;
        
        //
        // Storage for the clients
        //
        HashMap<String, Object> clients = new HashMap<>();
        AmazonIdentityManagement iamClient = AmazonIdentityManagementClientBuilder.standard().build();
        
        //
        // Upload the jar to S3
        //
        HashMap<String,Bucket> uploadBucket = new HashMap<>();
        for ( String region : regions.split(","))
        {
        	Regions regionEnum = Regions.fromName(region);
            AWSLambda lambdaClient = AWSLambdaClientBuilder.standard().withRegion(regionEnum).build();
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(regionEnum).build();
            AmazonApiGateway apiClient = AmazonApiGatewayClientBuilder.standard().withRegion(regionEnum).build();
            clients.put(region+"-lambda", lambdaClient);
            clients.put(region+"-s3", s3Client);
            clients.put(region+"-apigw", apiClient);
            
            //
            // Get the account number.
            //
            if ( accountNumber == null )
            {
	            GetUserResult guRes = iamClient.getUser();
	            String[] accountArn = guRes.getUser().getArn().split(":");
	            getLog().info("Account Number: " + accountArn[4]);
	            accountNumber = accountArn[4];
            }
            
            getLog().info("Environment: " + environment);
            String uploadJarBucketName = environment + "." + region + "." + uploadJarBucket;
            uploadJarBucketName = uploadJarBucketName.toLowerCase();
//	        s3Client.setRegion(Region.getRegion(Regions.fromName(region)));
            getLog().info("Checking if bucket " + uploadJarBucketName.toLowerCase() + " exists...");
	    	if ( !s3Client.doesBucketExist(uploadJarBucketName.toLowerCase()))	// If bucket doesn't exist, create it.
	    	{
	    		getLog().info("Creating bucket " + uploadJarBucketName);
	    		Bucket cbRes = null;
	    		try
	    		{
	    			cbRes = s3Client.createBucket(uploadJarBucketName);
	    		}
	    		catch ( Exception ex )
	    		{
	    			getLog().error("Caught exception creating bucket " + uploadJarBucketName);
	    			throw ex;
	    		}
//	    		uploadBucket.put(region, cbRes);
	    	}
	    	else
	    	{
	    		getLog().info("Bucket already exists");
	    	}
	
	    	getLog().info("Uploading Jar in " + region + "...");
	    	PutObjectRequest poReq = new PutObjectRequest(uploadJarBucketName, project.getArtifact().getFile().getName(), 
	    			project.getArtifact().getFile());
	    	poReq.setGeneralProgressListener(new UploadProgressListener());
	    	PutObjectResult poRes = null;
	    	
	    	try
	    	{
	    		getLog().info("Uploading JAR " + project.getArtifact().getFile().getName() + "...");
	    		poRes = s3Client.putObject(poReq);
	    	}
	    	catch ( Exception ex )
	    	{
    			getLog().error("Caught exception uploading JAR " + project.getArtifact().getFile());
	    		ex.printStackTrace();
	    		return;
	    	}
        }
        
    	//
    	// Deal with the role to be assumed
    	//
    	GetRoleRequest grReq = new GetRoleRequest()
    			.withRoleName(assumedRoleName);
    	boolean assumedRoleExists = true;
    	GetRoleResult grRes = null;
    	try
    	{
	    	grRes = iamClient.getRole(grReq);
	    	if ( grRes == null )
	    		getLog().info("Role " + assumedRoleName + " does not already exist, creating.");
	    	else
	    	{
	    		getLog().info("Role " + assumedRoleName + " already exists, updating.");
//	    		getLog().info("Status code: " + grRes.getSdkHttpMetadata().getHttpStatusCode());
//	    		getLog().info("grRes != null, Arn: " + grRes.getRole().getArn());
	    	}
    	}
    	catch ( Exception ex )
    	{
    		assumedRoleExists = false;
    	}
    	
    	String roleArn = null;
    	if ( assumedRoleExists )
    	{
    		roleArn = grRes.getRole().getArn();
    		
    		// Update the policy in case it changed
    		String policyArn = "arn:aws:iam::" + accountNumber + ":policy/" + serviceName + "_AssumedPolicy";
    		getLog().info("Detaching policy " + policyArn);
    		DetachRolePolicyRequest drpReq = new DetachRolePolicyRequest()
    				.withPolicyArn(policyArn)
    				.withRoleName(assumedRoleName);
    		DetachRolePolicyResult drpRes = null;
    		try
    		{
    			drpRes = iamClient.detachRolePolicy(drpReq);
    		} catch ( NoSuchEntityException nsee )
    		{
    			// Do nothing
    		} catch ( Exception ex ) 
    		{
    			ex.printStackTrace();
    		}
    		getLog().info("Deleting policy " + policyArn);
    		DeletePolicyRequest dpReq = new DeletePolicyRequest()
    				.withPolicyArn(policyArn);
    		try
    		{
    			DeletePolicyResult dpRes = iamClient.deletePolicy(dpReq);
    			int x = 1;
    		} catch ( Exception ex ) { ex.printStackTrace();}
    		sleep(5);
    	}
    	else
    	{
    		String assumeRolePolicy = "{\"Version\": \"2012-10-17\"," +
    				"\"Statement\": [{" +
    				"\"Sid\": \"\", " +
    				"\"Effect\": \"Allow\", " +
    				"\"Principal\": {\"Service\": \"lambda.amazonaws.com\"}, " +
    				"\"Action\": [\"sts:AssumeRole\"] " +
    				"}]}";
    		CreateRoleRequest crReq = new CreateRoleRequest()
    				.withRoleName(assumedRoleName);
    		crReq.setAssumeRolePolicyDocument(assumeRolePolicy);
    		
    		getLog().info("Creating role with policy: " + rolePolicy);
    		getLog().info("Trust policy: " + assumeRolePolicy);
    		CreateRoleResult crRes = iamClient.createRole(crReq);
    		roleArn = crRes.getRole().getArn();
    	}
    	
		// Create the custom policy
    	rolePolicy = "{" +
			"    \"Version\": \"2012-10-17\",\n" +
    		"    \"Statement\": [\n";

    	// Loop through all of the Permission entries
    	Permission [] perms = permissions.toArray(new Permission[0]);
    	String permPolicy = "";
    	try
    	{
	    	for ( int pi = 0; pi < perms.length; pi ++ )
	    	{
	    		Permission p = perms[pi];
	    		
	    		String permComma = pi == perms.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
	
	    		permPolicy = "{\"Effect\": \"" + p.effect + "\",";
	    		
		    	String[] regionList = regions.split(",");
		    	permPolicy += "\"Resource\": [";
		    	String tmp = "";
		    	for ( int i = 0; i < regionList.length; i ++ )
		    	{
		    		String region = regionList[i];
		    		String regionComma = i == regionList.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
		    		
		    		List<String> resourceList = p.resources;
		    		for ( String resource : resourceList )
		    		{ 
		    			tmp += ",\"" + resource + "\"";
		    		}
		    	}
		    	permPolicy += tmp.substring(1);
		    	permPolicy += "],";
	    		
	    		permPolicy += "\"Action\": [";
	    		String [] actionList = p.actions.toString().split(",");
	    		
	    		for ( int ai = 0; ai < actionList.length; ai ++ )
	    		{
	    			String action = actionList[ai];
		    		String actionComma = ai == actionList.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
		    		action = action.replace("[", "").replace("]", "").replace(" ", "");
	    			permPolicy += "\"" + action + "\"" + actionComma;
	    		}
	    		permPolicy += "]}" + permComma;
	    		rolePolicy += permPolicy;
	    		
	    	} // End of loop on Permission entries
    	}
    	catch ( Exception ex )
    	{
    		getLog().info("Caught ex: " + ex.getMessage() + "\nPermPolicy: " + permPolicy + "\nRolePolicy: " + rolePolicy);
    		ex.printStackTrace();
    	}
    	rolePolicy += 
			"    ]\n" +
			"}\n";
    	
    	// First update the variables
    	rolePolicy = rolePolicy.replace("$regions$", regions);
    	rolePolicy = rolePolicy.replace("$region$", regions);
    	rolePolicy = rolePolicy.replace("$accountId$", accountNumber);
    	rolePolicy = rolePolicy.replace("$serviceName$", serviceName);
    	rolePolicy = rolePolicy.replace("$environment$", environment);
    	
		String policyName = serviceName + "_AssumedPolicy";
		getLog().info("Creating custom policy: " + policyName);
		getLog().info("Creating custom policy: " + rolePolicy);
		CreatePolicyRequest cpReq = new CreatePolicyRequest()
				.withPolicyDocument(rolePolicy)
				.withPolicyName(policyName);
		CreatePolicyResult cpRes = null;
		try
		{
			cpRes = iamClient.createPolicy(cpReq);
			if ( cpRes.getSdkHttpMetadata().getHttpStatusCode() > 201 )
			{
				getLog().error("Error from CreatePolicy: " + cpRes.getSdkHttpMetadata().getHttpStatusCode());
				throw new Exception("Eror from CreatePolicy: " + cpRes.getSdkHttpMetadata().getHttpStatusCode());
			}
		}
		catch ( Exception ex )
		{
			getLog().error("Caught exception creating the Assume Policy: " + rolePolicy);
			ex.printStackTrace();
			return;
		}
		
		// Attach the custom policy
		try
		{
			getLog().info("Attaching Policy to Role...");
			AttachRolePolicyRequest arpReq = new AttachRolePolicyRequest()
					.withRoleName(assumedRoleName)
					.withPolicyArn(cpRes.getPolicy().getArn());
			AttachRolePolicyResult arpRes = iamClient.attachRolePolicy(arpReq);
		}
		catch ( Exception ex )
		{
			getLog().error(String.format("Caught exception Attaching Policy (arn: %s) to Role (name: %s): %s", cpRes.getPolicy().getArn(), assumedRoleName, ex.getMessage()));
			ex.printStackTrace();
			return;
		}
		//
    	// Upload the function
    	//
    	for ( String region : regions.split(",") )
    	{
    		getLog().debug("Processing for Region: " + region);
    		
    		// Set the client region
//        	lambdaClient.setRegion(Region.getRegion(Regions.fromName(region)));
//        	s3Client.setRegion(Region.getRegion(Regions.fromName(region)));
//        	apiClient.setRegion(Region.getRegion(Regions.fromName(region)));
            String uploadJarBucketName = environment + "." + region + "." + uploadJarBucket;
            uploadJarBucketName = uploadJarBucketName.toLowerCase();

            
	    	AWSLambda lambdaClient = (AWSLambda) clients.get(region+"-lambda");
	    	AmazonApiGateway apiClient = (AmazonApiGateway) clients.get(region+"-apigw");
	    	AmazonS3 s3Client = (AmazonS3) clients.get(region+"-s3");
	    	
			// Try deleting the function
	    	DeleteFunctionRequest dfReq = new DeleteFunctionRequest();
	    	dfReq.setFunctionName(serviceName);
	        DeleteFunctionResult dfRes = lambdaClient.deleteFunction(dfReq);
	    	getLog().info("Delete function returned: " + dfRes.getSdkHttpMetadata().getHttpStatusCode());
	    	
	    	// See if the function already exists
	        ListFunctionsResult lfRes = lambdaClient.listFunctions();
	        List<FunctionConfiguration> functions = lfRes.getFunctions();
	        boolean updateFunction = false;
	        
	        for ( FunctionConfiguration function : functions )
	        {
	        	getLog().debug("Found existing function: " + function.getFunctionName());
	        	if ( function.getFunctionName().equals(serviceName))
	        		updateFunction = true;
	        }

	        updateFunction = false;	// Because we deleted it
	        
	        // Create or update the function
	        String serviceArn = null;
	        if ( !updateFunction )
	        {
	        	getLog().info("Creating function!");
	        	
	            CreateFunctionRequest cfReq = new CreateFunctionRequest();<commit_msg>Ignore failed deletes<commit_after>package com.mcdaniel.serverless;


import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;

import com.amazonaws.event.ProgressEvent;
import com.amazonaws.event.ProgressEventType;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.apigateway.AmazonApiGateway;
import com.amazonaws.services.apigateway.AmazonApiGatewayClientBuilder;
import com.amazonaws.services.apigateway.model.ContentHandlingStrategy;
import com.amazonaws.services.apigateway.model.CreateResourceRequest;
import com.amazonaws.services.apigateway.model.CreateResourceResult;
import com.amazonaws.services.apigateway.model.DeleteResourceRequest;
import com.amazonaws.services.apigateway.model.DeleteResourceResult;
import com.amazonaws.services.apigateway.model.GetResourcesRequest;
import com.amazonaws.services.apigateway.model.GetResourcesResult;
import com.amazonaws.services.apigateway.model.GetRestApisRequest;
import com.amazonaws.services.apigateway.model.GetRestApisResult;
import com.amazonaws.services.apigateway.model.Integration;
import com.amazonaws.services.apigateway.model.IntegrationResponse;
import com.amazonaws.services.apigateway.model.IntegrationType;
import com.amazonaws.services.apigateway.model.Method;
import com.amazonaws.services.apigateway.model.PutIntegrationRequest;
import com.amazonaws.services.apigateway.model.PutIntegrationResult;
import com.amazonaws.services.apigateway.model.PutMethodRequest;
import com.amazonaws.services.apigateway.model.PutMethodResponseRequest;
import com.amazonaws.services.apigateway.model.PutMethodResponseResult;
import com.amazonaws.services.apigateway.model.PutMethodResult;
import com.amazonaws.services.apigateway.model.Resource;
import com.amazonaws.services.apigateway.model.RestApi;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagement;
import com.amazonaws.services.identitymanagement.AmazonIdentityManagementClientBuilder;
import com.amazonaws.services.identitymanagement.model.AttachRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.AttachRolePolicyResult;
import com.amazonaws.services.identitymanagement.model.CreatePolicyRequest;
import com.amazonaws.services.identitymanagement.model.CreatePolicyResult;
import com.amazonaws.services.identitymanagement.model.CreateRoleRequest;
import com.amazonaws.services.identitymanagement.model.CreateRoleResult;
import com.amazonaws.services.identitymanagement.model.DeletePolicyRequest;
import com.amazonaws.services.identitymanagement.model.DeletePolicyResult;
import com.amazonaws.services.identitymanagement.model.DetachRolePolicyRequest;
import com.amazonaws.services.identitymanagement.model.DetachRolePolicyResult;
import com.amazonaws.services.identitymanagement.model.GetRoleRequest;
import com.amazonaws.services.identitymanagement.model.GetRoleResult;
import com.amazonaws.services.identitymanagement.model.GetUserResult;
import com.amazonaws.services.identitymanagement.model.NoSuchEntityException;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.AddPermissionRequest;
import com.amazonaws.services.lambda.model.AddPermissionResult;
import com.amazonaws.services.lambda.model.CreateAliasRequest;
import com.amazonaws.services.lambda.model.CreateAliasResult;
import com.amazonaws.services.lambda.model.CreateFunctionRequest;
import com.amazonaws.services.lambda.model.CreateFunctionResult;
import com.amazonaws.services.lambda.model.DeleteFunctionRequest;
import com.amazonaws.services.lambda.model.DeleteFunctionResult;
import com.amazonaws.services.lambda.model.Environment;
import com.amazonaws.services.lambda.model.FunctionCode;
import com.amazonaws.services.lambda.model.FunctionConfiguration;
import com.amazonaws.services.lambda.model.GetPolicyRequest;
import com.amazonaws.services.lambda.model.GetPolicyResult;
import com.amazonaws.services.lambda.model.ListFunctionsResult;
import com.amazonaws.services.lambda.model.PublishVersionRequest;
import com.amazonaws.services.lambda.model.PublishVersionResult;
import com.amazonaws.services.lambda.model.RemovePermissionRequest;
import com.amazonaws.services.lambda.model.RemovePermissionResult;
import com.amazonaws.services.lambda.model.UpdateAliasRequest;
import com.amazonaws.services.lambda.model.UpdateAliasResult;
import com.amazonaws.services.lambda.model.UpdateFunctionCodeRequest;
import com.amazonaws.services.lambda.model.UpdateFunctionCodeResult;
import com.amazonaws.services.lambda.model.UpdateFunctionConfigurationRequest;
import com.amazonaws.services.lambda.model.UpdateFunctionConfigurationResult;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.mcdaniel.serverless.policy.LambdaPolicy;
import com.mcdaniel.serverless.policy.Statement;

import io.swagger.models.Operation;
import io.swagger.models.Path;
import io.swagger.models.Swagger;
import io.swagger.models.parameters.Parameter;
import io.swagger.parser.SwaggerParser;
 
/**
 * Says "Hi" to the user.
 *
 */
@Mojo( name = "deploy", defaultPhase=LifecyclePhase.DEPLOY, requiresOnline=true, requiresProject=true)
public class ServerlessDeployMojo extends BaseServerlessMojo
{
	
	private static final String DEFAULT_PRODUCES_CONTENT_TYPE = "application/json";
    private static final String EXTENSION_AUTH = "x-amazon-apigateway-auth";
    private static final String EXTENSION_INTEGRATION = "x-amazon-apigateway-integration";
    
    private Swagger swagger;
    
	public void execute() throws MojoExecutionException
    {
    	String rolePolicy = null;

    	getLog().info("serverless-maven-plugin:deploy starting");
        getLog().info("Logger class: " + getLog().getClass().getName());
        
        if ( project != null )
        {
        	getLog().info("Name/Packaging: " + project.getName() + " / " + project.getPackaging());
        	getLog().info("Artifact name: " + project.getArtifact().getFile().getName());
        }
        
        // Create the credentials for all of the clients.
//        getLog().info("AccessKey: " + AWSAccessKey);
//        BasicAWSCredentials awsCredentials = new BasicAWSCredentials(AWSAccessKey, AWSSecretKey);
        
        // Setup needed variables
        String assumedRoleName = serviceName + "_AssumedRole";
        
        // Create all of the needed clients.
        getLog().info("Getting clients...");
//        AWSLambdaClient lambdaClient = new AWSLambdaClient(awsCredentials);
//        AmazonS3Client s3Client = new AmazonS3Client(awsCredentials);
//        AmazonIdentityManagementClient iamClient = new AmazonIdentityManagementClient(awsCredentials);
//        AmazonApiGatewayClient apiClient = new AmazonApiGatewayClient(awsCredentials);
        

        getLog().info("Done.");
        
        String accountNumber = null;
        
        //
        // Storage for the clients
        //
        HashMap<String, Object> clients = new HashMap<>();
        AmazonIdentityManagement iamClient = AmazonIdentityManagementClientBuilder.standard().build();
        
        //
        // Upload the jar to S3
        //
        HashMap<String,Bucket> uploadBucket = new HashMap<>();
        for ( String region : regions.split(","))
        {
        	Regions regionEnum = Regions.fromName(region);
            AWSLambda lambdaClient = AWSLambdaClientBuilder.standard().withRegion(regionEnum).build();
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(regionEnum).build();
            AmazonApiGateway apiClient = AmazonApiGatewayClientBuilder.standard().withRegion(regionEnum).build();
            clients.put(region+"-lambda", lambdaClient);
            clients.put(region+"-s3", s3Client);
            clients.put(region+"-apigw", apiClient);
            
            //
            // Get the account number.
            //
            if ( accountNumber == null )
            {
	            GetUserResult guRes = iamClient.getUser();
	            String[] accountArn = guRes.getUser().getArn().split(":");
	            getLog().info("Account Number: " + accountArn[4]);
	            accountNumber = accountArn[4];
            }
            
            getLog().info("Environment: " + environment);
            String uploadJarBucketName = environment + "." + region + "." + uploadJarBucket;
            uploadJarBucketName = uploadJarBucketName.toLowerCase();
//	        s3Client.setRegion(Region.getRegion(Regions.fromName(region)));
            getLog().info("Checking if bucket " + uploadJarBucketName.toLowerCase() + " exists...");
	    	if ( !s3Client.doesBucketExist(uploadJarBucketName.toLowerCase()))	// If bucket doesn't exist, create it.
	    	{
	    		getLog().info("Creating bucket " + uploadJarBucketName);
	    		Bucket cbRes = null;
	    		try
	    		{
	    			cbRes = s3Client.createBucket(uploadJarBucketName);
	    		}
	    		catch ( Exception ex )
	    		{
	    			getLog().error("Caught exception creating bucket " + uploadJarBucketName);
	    			throw ex;
	    		}
//	    		uploadBucket.put(region, cbRes);
	    	}
	    	else
	    	{
	    		getLog().info("Bucket already exists");
	    	}
	
	    	getLog().info("Uploading Jar in " + region + "...");
	    	PutObjectRequest poReq = new PutObjectRequest(uploadJarBucketName, project.getArtifact().getFile().getName(), 
	    			project.getArtifact().getFile());
	    	poReq.setGeneralProgressListener(new UploadProgressListener());
	    	PutObjectResult poRes = null;
	    	
	    	try
	    	{
	    		getLog().info("Uploading JAR " + project.getArtifact().getFile().getName() + "...");
	    		poRes = s3Client.putObject(poReq);
	    	}
	    	catch ( Exception ex )
	    	{
    			getLog().error("Caught exception uploading JAR " + project.getArtifact().getFile());
	    		ex.printStackTrace();
	    		return;
	    	}
        }
        
    	//
    	// Deal with the role to be assumed
    	//
    	GetRoleRequest grReq = new GetRoleRequest()
    			.withRoleName(assumedRoleName);
    	boolean assumedRoleExists = true;
    	GetRoleResult grRes = null;
    	try
    	{
	    	grRes = iamClient.getRole(grReq);
	    	if ( grRes == null )
	    		getLog().info("Role " + assumedRoleName + " does not already exist, creating.");
	    	else
	    	{
	    		getLog().info("Role " + assumedRoleName + " already exists, updating.");
//	    		getLog().info("Status code: " + grRes.getSdkHttpMetadata().getHttpStatusCode());
//	    		getLog().info("grRes != null, Arn: " + grRes.getRole().getArn());
	    	}
    	}
    	catch ( Exception ex )
    	{
    		assumedRoleExists = false;
    	}
    	
    	String roleArn = null;
    	if ( assumedRoleExists )
    	{
    		roleArn = grRes.getRole().getArn();
    		
    		// Update the policy in case it changed
    		String policyArn = "arn:aws:iam::" + accountNumber + ":policy/" + serviceName + "_AssumedPolicy";
    		getLog().info("Detaching policy " + policyArn);
    		DetachRolePolicyRequest drpReq = new DetachRolePolicyRequest()
    				.withPolicyArn(policyArn)
    				.withRoleName(assumedRoleName);
    		DetachRolePolicyResult drpRes = null;
    		try
    		{
    			drpRes = iamClient.detachRolePolicy(drpReq);
    		} catch ( NoSuchEntityException nsee )
    		{
    			// Do nothing
    		} catch ( Exception ex ) 
    		{
    			ex.printStackTrace();
    		}
    		getLog().info("Deleting policy " + policyArn);
    		DeletePolicyRequest dpReq = new DeletePolicyRequest()
    				.withPolicyArn(policyArn);
    		try
    		{
    			DeletePolicyResult dpRes = iamClient.deletePolicy(dpReq);
    			int x = 1;
    		} catch ( Exception ex ) { ex.printStackTrace();}
    		sleep(5);
    	}
    	else
    	{
    		String assumeRolePolicy = "{\"Version\": \"2012-10-17\"," +
    				"\"Statement\": [{" +
    				"\"Sid\": \"\", " +
    				"\"Effect\": \"Allow\", " +
    				"\"Principal\": {\"Service\": \"lambda.amazonaws.com\"}, " +
    				"\"Action\": [\"sts:AssumeRole\"] " +
    				"}]}";
    		CreateRoleRequest crReq = new CreateRoleRequest()
    				.withRoleName(assumedRoleName);
    		crReq.setAssumeRolePolicyDocument(assumeRolePolicy);
    		
    		getLog().info("Creating role with policy: " + rolePolicy);
    		getLog().info("Trust policy: " + assumeRolePolicy);
    		CreateRoleResult crRes = iamClient.createRole(crReq);
    		roleArn = crRes.getRole().getArn();
    	}
    	
		// Create the custom policy
    	rolePolicy = "{" +
			"    \"Version\": \"2012-10-17\",\n" +
    		"    \"Statement\": [\n";

    	// Loop through all of the Permission entries
    	Permission [] perms = permissions.toArray(new Permission[0]);
    	String permPolicy = "";
    	try
    	{
	    	for ( int pi = 0; pi < perms.length; pi ++ )
	    	{
	    		Permission p = perms[pi];
	    		
	    		String permComma = pi == perms.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
	
	    		permPolicy = "{\"Effect\": \"" + p.effect + "\",";
	    		
		    	String[] regionList = regions.split(",");
		    	permPolicy += "\"Resource\": [";
		    	String tmp = "";
		    	for ( int i = 0; i < regionList.length; i ++ )
		    	{
		    		String region = regionList[i];
		    		String regionComma = i == regionList.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
		    		
		    		List<String> resourceList = p.resources;
		    		for ( String resource : resourceList )
		    		{ 
		    			tmp += ",\"" + resource + "\"";
		    		}
		    	}
		    	permPolicy += tmp.substring(1);
		    	permPolicy += "],";
	    		
	    		permPolicy += "\"Action\": [";
	    		String [] actionList = p.actions.toString().split(",");
	    		
	    		for ( int ai = 0; ai < actionList.length; ai ++ )
	    		{
	    			String action = actionList[ai];
		    		String actionComma = ai == actionList.length-1 ? "" : ",";	// If we're at the end, comma="", else comma=","
		    		action = action.replace("[", "").replace("]", "").replace(" ", "");
	    			permPolicy += "\"" + action + "\"" + actionComma;
	    		}
	    		permPolicy += "]}" + permComma;
	    		rolePolicy += permPolicy;
	    		
	    	} // End of loop on Permission entries
    	}
    	catch ( Exception ex )
    	{
    		getLog().info("Caught ex: " + ex.getMessage() + "\nPermPolicy: " + permPolicy + "\nRolePolicy: " + rolePolicy);
    		ex.printStackTrace();
    	}
    	rolePolicy += 
			"    ]\n" +
			"}\n";
    	
    	// First update the variables
    	rolePolicy = rolePolicy.replace("$regions$", regions);
    	rolePolicy = rolePolicy.replace("$region$", regions);
    	rolePolicy = rolePolicy.replace("$accountId$", accountNumber);
    	rolePolicy = rolePolicy.replace("$serviceName$", serviceName);
    	rolePolicy = rolePolicy.replace("$environment$", environment);
    	
		String policyName = serviceName + "_AssumedPolicy";
		getLog().info("Creating custom policy: " + policyName);
		getLog().info("Creating custom policy: " + rolePolicy);
		CreatePolicyRequest cpReq = new CreatePolicyRequest()
				.withPolicyDocument(rolePolicy)
				.withPolicyName(policyName);
		CreatePolicyResult cpRes = null;
		try
		{
			cpRes = iamClient.createPolicy(cpReq);
			if ( cpRes.getSdkHttpMetadata().getHttpStatusCode() > 201 )
			{
				getLog().error("Error from CreatePolicy: " + cpRes.getSdkHttpMetadata().getHttpStatusCode());
				throw new Exception("Eror from CreatePolicy: " + cpRes.getSdkHttpMetadata().getHttpStatusCode());
			}
		}
		catch ( Exception ex )
		{
			getLog().error("Caught exception creating the Assume Policy: " + rolePolicy);
			ex.printStackTrace();
			return;
		}
		
		// Attach the custom policy
		try
		{
			getLog().info("Attaching Policy to Role...");
			AttachRolePolicyRequest arpReq = new AttachRolePolicyRequest()
					.withRoleName(assumedRoleName)
					.withPolicyArn(cpRes.getPolicy().getArn());
			AttachRolePolicyResult arpRes = iamClient.attachRolePolicy(arpReq);
		}
		catch ( Exception ex )
		{
			getLog().error(String.format("Caught exception Attaching Policy (arn: %s) to Role (name: %s): %s", cpRes.getPolicy().getArn(), assumedRoleName, ex.getMessage()));
			ex.printStackTrace();
			return;
		}
		//
    	// Upload the function
    	//
    	for ( String region : regions.split(",") )
    	{
    		getLog().debug("Processing for Region: " + region);
    		
    		// Set the client region
//        	lambdaClient.setRegion(Region.getRegion(Regions.fromName(region)));
//        	s3Client.setRegion(Region.getRegion(Regions.fromName(region)));
//        	apiClient.setRegion(Region.getRegion(Regions.fromName(region)));
            String uploadJarBucketName = environment + "." + region + "." + uploadJarBucket;
            uploadJarBucketName = uploadJarBucketName.toLowerCase();

            
	    	AWSLambda lambdaClient = (AWSLambda) clients.get(region+"-lambda");
	    	AmazonApiGateway apiClient = (AmazonApiGateway) clients.get(region+"-apigw");
	    	AmazonS3 s3Client = (AmazonS3) clients.get(region+"-s3");
	    	
			// Try deleting the function
	    	DeleteFunctionRequest dfReq = new DeleteFunctionRequest();
	    	dfReq.setFunctionName(serviceName);
	    	try
	    	{
	    		DeleteFunctionResult dfRes = lambdaClient.deleteFunction(dfReq);
	    		getLog().info("Delete function returned: " + dfRes.getSdkHttpMetadata().getHttpStatusCode());
	    	}
	    	catch ( Exception ex )
	    	{
	    		// DO NOTHING.
	    	}
	    	
	    	// See if the function already exists
	        ListFunctionsResult lfRes = lambdaClient.listFunctions();
	        List<FunctionConfiguration> functions = lfRes.getFunctions();
	        boolean updateFunction = false;
	        
	        for ( FunctionConfiguration function : functions )
	        {
	        	getLog().debug("Found existing function: " + function.getFunctionName());
	        	if ( function.getFunctionName().equals(serviceName))
	        		updateFunction = true;
	        }

	        updateFunction = false;	// Because we deleted it
	        
	        // Create or update the function
	        String serviceArn = null;
	        if ( !updateFunction )
	        {
	        	getLog().info("Creating function!");
	        	
	            CreateFunctionRequest cfReq = new CreateFunctionRequest();
{'fraction_non_alphanumeric': 0.12233129492543718, 'fraction_numerical': 0.003003969531166184, 'mean_word_length': 3.2115667005534845, 'pattern_counts': {'":': 28, '<': 21, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5624257', 'n_tokens_mistral': 11448, 'n_tokens_neox': 10940, 'n_words': 1875}