/*
 * Copyright (c) 2017 Nike, 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.nike.cerberus.operation.core;

import com.amazonaws.AmazonServiceException;
import com.nike.cerberus.command.core.UpdateStackCommand;
import com.nike.cerberus.domain.environment.Stack;
import com.nike.cerberus.operation.Operation;
import com.nike.cerberus.service.CloudFormationService;
import com.nike.cerberus.service.Ec2UserDataService;
import com.nike.cerberus.store.ConfigStore;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.Map;
import java.util.Optional;

import static com.nike.cerberus.module.CerberusModule.ENV_NAME;

/**
 * Operation for updating stacks.
 */
public class UpdateStackOperation implements Operation<UpdateStackCommand> {


    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final CloudFormationService cloudFormationService;
    private final Ec2UserDataService ec2UserDataService;
    private final String environmentName;
    private final ConfigStore configStore;

    @Inject
    public UpdateStackOperation(CloudFormationService cloudFormationService,
                                Ec2UserDataService ec2UserDataService,
                                @Named(ENV_NAME) String environmentName,
                                ConfigStore configStore) {

        this.cloudFormationService = cloudFormationService;
        this.ec2UserDataService = ec2UserDataService;
        this.environmentName = environmentName;


        this.configStore = configStore;
    }

    @Override
    public void run(UpdateStackCommand command) {
        String stackId = command.getStack().getFullName(environmentName);

        Map<String, String> parameters = cloudFormationService.getStackParameters(configStore.getPrimaryRegion(), stackId);
        Map<String, String> tags = command.getTagsDelegate().getTags();

        // only some stacks need user data
        if (command.getStack().needsUserData()) {
            parameters.put("userData", ec2UserDataService.getUserData(configStore.getPrimaryRegion(), command.getStack(),
                    Optional.ofNullable(tags.getOrDefault("ownerGroup", null))));
        }

        if (Stack.CMS.equals(command.getStack())) {
            // TODO: tag check
        } else if (Stack.DATABASE.equals(command.getStack())) {
            // TODO: implement storing password if it was changed
            //configStore.storeCmsDatabasePassword(databasePassword);
        } else if (Stack.LOAD_BALANCER.equals(command.getStack())) {
            parameters.put("sslCertificateArn", configStore.getCertificationInformationList()
                    .getLast().getIdentityManagementCertificateArn());
        }
        parameters.putAll(command.getDynamicParameters());

        try {
            logger.info("Starting the update for '{}' overwrite:{}.", stackId, command.isOverwriteTemplate());

            cloudFormationService.updateStackAndWait(
                    configStore.getPrimaryRegion(),
                    command.getStack(),
                    parameters,
                    true,
                    command.isOverwriteTemplate(),
                    command.getTagsDelegate().getTags()
            );

            logger.info("Update complete.");
        } catch (AmazonServiceException ase) {
            if (ase.getStatusCode() == 400 &&
                    StringUtils.equalsIgnoreCase(ase.getErrorMessage(), "No updates are to be performed.")) {
                logger.warn("CloudFormation reported no changes detected.");
            } else {
                throw ase;
            }
        }
    }

    @Override
    public boolean isRunnable(UpdateStackCommand command) {
        boolean isRunnable = true;

        String fullName = command.getStack().getFullName(environmentName);
        if (!cloudFormationService.isStackPresent(configStore.getPrimaryRegion(), fullName)) {
            logger.error("CloudFormation doesn't have the specified stack: {}", fullName);
            isRunnable = false;
        }

        if (command.getStack().equals(Stack.LOAD_BALANCER) && configStore.getCertificationInformationList().isEmpty()) {
            logger.error("Updating the load balancer requires that a cert has been uploaded by the upload-certificate-files command");
            isRunnable = false;
        }

        return isRunnable;
    }
}
@@ -734,16 +734,83 @@
 ommand;%0A
+import com.nike.cerberus.domain.cloudformation.DatabaseParameters;%0A
 import c
@@ -894,24 +894,77 @@
 .Operation;%0A
+import com.nike.cerberus.service.AmiTagCheckService;%0A
 import com.n
@@ -1774,16 +1774,73 @@
 igStore;
+%0A    private final AmiTagCheckService amiTagCheckService;
 %0A%0A    @I
@@ -2066,16 +2066,87 @@
 ntName,%0A
+                                AmiTagCheckService amiTagCheckService,%0A
         
@@ -2359,17 +2359,69 @@
 ntName;%0A
-%0A
+        this.amiTagCheckService = amiTagCheckService;
 %0A       
@@ -3172,156 +3172,412 @@
     
-// TODO: tag check%0A        %7D else if (Stack.DATABASE.equals(command.getStack())) %7B%0A            // TODO: implement storing password if it was changed
+command.getDynamicParameters().forEach((key, value) -%3E %7B%0A                if (key.equals(%22amiId%22)) %7B%0A                    amiTagCheckService.validateAmiTagForStack(value, Stack.CMS);%0A                %7D%0A            %7D);%0A        %7D else if (Stack.DATABASE.equals(command.getStack())) %7B%0A            command.getDynamicParameters().forEach((key, value) -%3E %7B%0A                if (key.equals(%22cmsDbMasterPassword%22)) %7B
 %0A   
@@ -3585,18 +3585,24 @@
         
-//
+        
 configSt
@@ -3634,24 +3634,47 @@
 ord(
-databasePassword
+value);%0A                %7D%0A            %7D
 );%0A 

Implement special cases for update stack command