// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX - License - Identifier: Apache - 2.0

#include <iostream>
#include <string>
#include <aws/core/Aws.h>
#include <aws/acm/ACMClient.h>
#include <aws/acm/model/RequestCertificateRequest.h>
#include <aws/core/utils/Outcome.h>
#include <awsdoc/acm/acm_examples.h>

const char* PROGRAM_NAME = "test_acm_basic_operations";
const char* TEST_SUITE_NAME = "Certificate Manager operations test";

// Prints command-line usage information.
void PrintUsage(const char* programName)
{
    std::cout << "Usage: " << programName << " [OPTION ARGUMENT]" << std::endl;
    std::cout << "--mode issue-enabled: Specify only if you are requesting a production-ready certificate." << std::endl;
    std::cout << "--type private:       Specify only if you are requesting a private certificate. Must be used with --mode." << std::endl;
    std::cout << "--help:               Print this usage message." << std::endl;
    std::cout << "Example 1: " << programName << std::endl;
    std::cout << "Example 2: " << programName << " --mode issue-enabled" << std::endl;
    std::cout << "Example 3: " << programName << " --mode issue-enabled --type private" << std::endl;
    std::cout << "Example 4: " << programName << " --help" << std::endl;

    return;
}

// Prints instructions for cleaning up orphaned AWS resources if any test 
// in this test suite fails.
void PrintCleanUpMessage(const char* unitTestName,
    const Aws::String& certificateArn)
{
    std::cout << "Error: " << TEST_SUITE_NAME << ": " << unitTestName <<
        ". To clean up, delete the certificate with the ARN '" <<
        certificateArn << "' yourself." << std::endl << std::endl;

    return;
}

// All tests in this test suite cannot successfully run by default. To do so, 
// if you have the ability to request production-ready certificates, 
// then call this test executable with the "--mode issue-enabled" option.
// Additionally, if you have the ability to request private certificates, 
// then call this test executable with the 
// "--mode issue-enabled --type private" options.
int main(int argc, char* argv[])
{
    bool issue_enabled = false;
    bool is_private = false;

    // Check to see whether correct command-line arguments were supplied.
    if ((argc > 5) ||                                               // More than 5 arguments.
        (argc == 4) ||                                              // Only 4 arguments; not allowed.
        (argc == 2) ||                                              // Seeking help or incorrect syntax.
        ((argc == 3) && (strcmp(argv[1], "--mode") != 0)) ||        // Wrong order of arguments.
        ((argc == 3) && (strcmp(argv[2], "issue-enabled") != 0)) || // Incorrect syntax for --mode.
        ((argc == 5) && (strcmp(argv[3], "--type") != 0) ||         // Incorrect syntax for --type.
        ((argc == 5) && (strcmp(argv[4], "private") != 0))))        // Still incorrect syntax for --type.
    {
        std::cout << "Error: Cannot run tests." << std::endl;

        PrintUsage(PROGRAM_NAME);

        return 1;
    }

    if (argc == 3) // "--mode issue-enabled" specified.
    {
        // Enable unit tests for UpdateCertificateOption, 
        // ResendValidationEmail, GetCertificate, and 
        // RenewCertificate.
        issue_enabled = true;
    }
    
    if (argc == 5) // "--mode issue-enabled --type private" specified.
    {
        // Also enable unit test for ExportCertificate.
        issue_enabled = true;
        is_private = true;
    }

    Aws::SDKOptions options;
    Aws::InitAPI(options);
    {
        const Aws::String region = "us-east-1";
        const Aws::String domain_name = "www.example.com";
        const Aws::String idempotency_token = "54321";
        const Aws::String tag_key = "my-key";
        const Aws::String tag_value = "my-value";

        // Request (create) a new certificate.
        std::cout << "Requesting new certificate with domain name '" <<
            domain_name << "'..." << std::endl << std::endl;

        Aws::String certificate_arn;

        Aws::Client::ClientConfiguration config;
        config.region = region;

        Aws::ACM::ACMClient acm_client(config);

        Aws::ACM::Model::RequestCertificateRequest request;
        request.WithDomainName(domain_name)
            .WithIdempotencyToken(idempotency_token);

        Aws::ACM::Model::RequestCertificateOutcome outcome =
            acm_client.RequestCertificate(request);

        if (!outcome.IsSuccess())
        {
            std::cout << "Error: " << TEST_SUITE_NAME << ": RequestCertificate: "
                << outcome.GetError().GetMessage() << std::endl;
            std::cout << "No cleanup needed. Check acm_basic_operations.cpp for "
                "possible issues with the AwsDoc::ACM::RequestCertificate "
                "helper method." << std::endl;

            return 1;
        }
        else
        {
            certificate_arn = outcome.GetResult().GetCertificateArn();

            std::cout << "Certificate successfully requested; ARN is '" <<
                certificate_arn << "'." << std::endl << std::endl;


        }

        // Change the new certificate's logging options.
        if (issue_enabled)
        {
            std::cout << "Updating logging options for certificate with ARN '" <<
                certificate_arn << "'..." << std::endl << std::endl;

            if (!AwsDoc::ACM::UpdateCertificateOption(certificate_arn, region, "logging-enabled"))
            {
                PrintCleanUpMessage("UpdateCertificateOption", certificate_arn);

                return 1;
            }
        }
        else
        {
            std::cout << "Skipping test for UpdateCertificateOption, as issue-enabled option is unset." << 
                std::endl << std::endl;
        }

        // Resend the validation email for the new certificate.
        if (issue_enabled)
        {
            std::cout << "Resending validation email for certificate with ARN '" <<
                certificate_arn << "' to domain '" << domain_name <<
                "'..." << std::endl << std::endl;

            if (!AwsDoc::ACM::ResendValidationEmail(certificate_arn, domain_name,
                domain_name, region))
            {
                PrintCleanUpMessage("ResendValidationEmail", certificate_arn);

                return 1;
            }
        }
        else
        {
            std::cout << "Skipping test for ResendValidationEmail, as issue-enabled option is unset." <<
                std::endl << std::endl;
        }

        // Add a tag to the certificate.
        std::cout << "Adding tag with key '" << tag_key << "' and value '" <<
            tag_value << "' to certificate with ARN '" <<
            certificate_arn << "'..." << std::endl << std::endl;

        if (!AwsDoc::ACM::AddTagToCertificate(certificate_arn, tag_key, tag_value,
            region))
        {

            PrintCleanUpMessage("AddTagToCertificate", certificate_arn);

            return 1;
        }
        
        // Get general information about the certificate.
        std::cout << std::endl << "Getting general information about certificate with ARN '" <<
            certificate_arn << "'..." << std::endl << std::endl;

        if (!AwsDoc::ACM::DescribeCertificate(certificate_arn, region))
        {
            PrintCleanUpMessage("DescribeCertificate", certificate_arn);

            return 1;
        }

        // Get information about available tags for the certificate. 
        std::cout << std::endl << "Getting information about tags for certificate with ARN '" <<
            certificate_arn << "'..." << std::endl << std::endl;

        if (!AwsDoc::ACM::ListTagsForCertificate(certificate_arn, region))
        {
            PrintCleanUpMessage("ListTagsForCertificate", certificate_arn);

            return 1;
        }

        // Get information about the new certificate's certificate and 
        // certificate chain.
        if (issue_enabled)
        {
            std::cout << "Getting information about the certificate and certificate "
                "chain for certificate with ARN '" <<
                certificate_arn << "'..." << std::endl << std::endl;

            if (!AwsDoc::ACM::GetCertificate(certificate_arn, region))
            {
                PrintCleanUpMessage("GetCertificate", certificate_arn);

                return 1;
            }
        }
        else
        {
            std::cout << "Skipping test for GetCertificate, as issue-enabled option is unset." <<
                std::endl << std::endl;
        }

        // Get PEM-based certificate, private key, and certificate chain 
        // information about the certificate. 
        if (is_private)
        {
            std::cout << "Getting PEM-based information about the certificate, "
                "private key, and certificate chain for certificate with ARN '" <<
                certificate_arn << "'..." << std::endl << std::endl;

            if (!AwsDoc::ACM::ExportCertificate(certificate_arn, region))
            {
                PrintCleanUpMessage("ExportCertificate", certificate_arn);

                return 1;
            }
        }
        else
        {
            std::cout << "Skipping test for ExportCertificate, as is-private option is unset." <<
                std::endl << std::endl;
        }

        // Remove the previous tag from the certificate.
        std::cout << "Removing tag with key '" << tag_key << "' from certificate with ARN '" <<
            certificate_arn << "'..." << std::endl << std::endl;

        if (!AwsDoc::ACM::RemoveTagFromCertificate(certificate_arn, tag_key, region))
        {
            PrintCleanUpMessage("RemoveTagFromCertificate", certificate_arn);

            return 1;
        }

        // Import a certificate.
        std::cout << std::endl << "Skipping test for ImportCertificate, as "
            "running this test requires prior availability of a PEM-formatted certificate file or string, "
            "a PEM-formatted private key file or string, and a PEM-formatted certificate chain file or string." << 
            std::endl << std::endl;

        // List available certificates.
        std::cout << "Listing available certificates..." << std::endl << std::endl;

        if (!AwsDoc::ACM::ListCertificates(region))
        {
            PrintCleanUpMessage("ListCertificates", certificate_arn);

            return 1;
        }

        // Renew a certificate.
        if (issue_enabled)
        {
            std::cout << "Renewing certificate with ARN '" <<
                certificate_arn << "'..." << std::endl << std::endl;

            if (!AwsDoc::ACM::RenewCertificate(certificate_arn, region))
            {
                PrintCleanUpMessage("RenewCertificate", certificate_arn);

                return 1;
            }
        }
        else
        {
            std::cout << "Skipping test for RenewCertificate, as issue-enabled option is unset." <<
                std::endl << std::endl;
        }

        // Delete the certificate.
        std::cout << "Deleting certificate with ARN '" <<
            certificate_arn << "'..." << std::endl << std::endl;

        if (!AwsDoc::ACM::DeleteCertificate(certificate_arn, region))
        {
            PrintCleanUpMessage("DeleteCertificate", certificate_arn);

            return 1;
        }
    }
    Aws::ShutdownAPI(options);

    return 0;
}