package com.amazonaws.resiliencehub.app;

import org.apache.commons.lang3.Validate;

import com.amazonaws.resiliencehub.common.TaggingUtil;

import software.amazon.awssdk.services.resiliencehub.ResiliencehubClient;
import software.amazon.cloudformation.proxy.AmazonWebServicesClientProxy;
import software.amazon.cloudformation.proxy.Logger;
import software.amazon.cloudformation.proxy.ProgressEvent;
import software.amazon.cloudformation.proxy.ProxyClient;
import software.amazon.cloudformation.proxy.ResourceHandlerRequest;

public class CreateHandler extends BaseHandlerStd {

    static final int CALLBACK_DELAY_SECONDS = 1;

    private Logger logger;
    private final TaggingUtil taggingUtil;

    public CreateHandler() {
        super();
        this.taggingUtil = new TaggingUtil();
    }

    public CreateHandler(final ApiCallsWrapper apiCallsWrapper, final TaggingUtil taggingUtil) {
        super(apiCallsWrapper);
        Validate.notNull(taggingUtil);

        this.taggingUtil = taggingUtil;
    }

    @Override
    protected ProgressEvent<ResourceModel, CallbackContext> handleRequest(
        final AmazonWebServicesClientProxy proxy,
        final ResourceHandlerRequest<ResourceModel> request,
        final CallbackContext callbackContext,
        final ProxyClient<ResiliencehubClient> proxyClient,
        final Logger logger) {

        this.logger = logger;

        // https://github.com/aws-cloudformation/cloudformation-cli-java-plugin/blob/master/src/main/java/software/amazon/cloudformation/proxy/CallChain.java
        return ProgressEvent.progress(request.getDesiredResourceState(), callbackContext)
            .then(progress -> createApp(proxy, proxyClient, progress.getCallbackContext(), progress.getResourceModel()))
            .then(progress -> addTemplate(proxy, proxyClient, progress.getCallbackContext(), progress.getResourceModel()))
            .then(progress -> addResourceMappings(proxy, proxyClient, progress.getCallbackContext(), progress.getResourceModel()))
            .then(progress -> publishVersion(proxy, proxyClient, progress.getCallbackContext(), progress.getResourceModel()))
            // Describe call/chain to return the resource model
            .then(progress -> new ReadHandler(apiCallsWrapper, taggingUtil)
                .handleRequest(proxy, request, callbackContext, proxyClient, logger));
    }

    private ProgressEvent<ResourceModel, CallbackContext> createApp(
        final AmazonWebServicesClientProxy proxy,
        final ProxyClient<ResiliencehubClient> proxyClient,
        final CallbackContext callbackContext,
        final ResourceModel model) {
        logger.log(String.format("isCreatedFlag: %s", callbackContext.isCreated()));
        if (callbackContext.isCreated()) {
            logger.log("AWS::ResilienceHub::App was already created, will not call CreateApp again.");
            return ProgressEvent.progress(model, callbackContext);
        }
        return proxy.initiate("AWS-ResilienceHub-App::create-app", proxyClient, model, callbackContext)
            .translateToServiceRequest(Translator::translateToCreateAppRequest)
            .makeServiceCall(apiCallsWrapper::createApp)
            .done(createAppResponse -> {
                model.setAppArn(createAppResponse.app().appArn());
                callbackContext.setCreated(true);
                logger.log(String.format("Successfully created app [%s] for resourceType %s. Continuing further..", model.getName(),
                    ResourceModel.TYPE_NAME));
                return ProgressEvent.defaultInProgressHandler(callbackContext, CALLBACK_DELAY_SECONDS, model);
            });
    }

    private ProgressEvent<ResourceModel, CallbackContext> addTemplate(
        final AmazonWebServicesClientProxy proxy,
        final ProxyClient<ResiliencehubClient> proxyClient,
        final CallbackContext callbackContext,
        final ResourceModel model) {
        return proxy.initiate("AWS-ResilienceHub-App::add-template", proxyClient, model, callbackContext)
            .translateToServiceRequest(Translator::translateToPutDraftAppVersionTemplateRequest)
            .makeServiceCall(apiCallsWrapper::putDraftAppVersionTemplate)
            .done(putDraftAppVersionTemplateResponse -> {
                logger.log(String.format("Successfully added template to %s [%s].",
                    ResourceModel.TYPE_NAME, model.getName()));
                return ProgressEvent.progress(model, callbackContext);
            });
    }

    private ProgressEvent<ResourceModel, CallbackContext> addResourceMappings(
        final AmazonWebServicesClientProxy proxy,
        final ProxyClient<ResiliencehubClient> proxyClient,
        final CallbackContext callbackContext,
        final ResourceModel model) {
        return proxy.initiate("AWS-ResilienceHub-App::add-resource-mappings", proxyClient, model, callbackContext)
            .translateToServiceRequest(Translator::translateToAddDraftAppVersionResourceMappingsRequest)
            .makeServiceCall(apiCallsWrapper::addDraftAppVersionResourceMappings)
            .done(addDraftAppVersionResourceMappingsResponse -> {
                logger.log(String.format("Successfully added resource mappings to %s [%s].",
                    ResourceModel.TYPE_NAME, model.getName()));
                return ProgressEvent.progress(model, callbackContext);
            });
    }

    private ProgressEvent<ResourceModel, CallbackContext> publishVersion(
        final AmazonWebServicesClientProxy proxy,
        final ProxyClient<ResiliencehubClient> proxyClient,
        final CallbackContext callbackContext,
        final ResourceModel model) {
        return proxy.initiate("AWS-ResilienceHub-App::publish-version", proxyClient, model, callbackContext)
            .translateToServiceRequest(Translator::translateToPublishAppVersionRequest)
            .makeServiceCall(apiCallsWrapper::publishAppVersion)
            .done(publishAppVersionResponse -> {
                logger.log(String
                    .format("Successfully published version [%s] for app [%s]. This completes the CREATE for resource type %s.",
                        publishAppVersionResponse.appVersion(), model.getName(), ResourceModel.TYPE_NAME));
                return ProgressEvent.progress(model, callbackContext);
            });
    }
}