/**
 *  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
 *  with the License. A copy of the License is located at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
 *  OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
 *  and limitations under the License.
 */

// Imports
const fs = require('fs');
const path = require('path');

// Paths
const global_s3_assets = path.join(__dirname, '../global-s3-assets');

// For each template in global_s3_assets ...
fs.readdirSync(global_s3_assets).forEach((file) => {
    // Import and parse template file
    const raw_template = fs.readFileSync(path.join(global_s3_assets, file));
    let template = JSON.parse(raw_template);
    const resources = template.Resources ? template.Resources : {};

    // Nested Stack clean-up
    const nestedStacks = Object.values(resources).filter(
        (value) => value.Type === "AWS::CloudFormation::Stack"
    );

    // Map nested stack resource names to the template file names.
    const nestedStackNames = {
        MediaInsightsWorkflowApi: "workflow-api-stack",
        MediaInsightsDataplaneApiStack: "dataplane-api-stack",
        Analytics: "dataplane-streaming-stack",
        OperatorLibrary: "operator-library",
        TestResources: "test-operations-stack"
    };
    const regexNestedStackNames = new RegExp(`^([^/]*)/.*(${Object.keys(nestedStackNames).join('|')}).*$`);

    // Rewrite the TemplateUrl for each nested stack.
    nestedStacks.forEach(function (stack) {
        const name = stack.Metadata['aws:cdk:path'].replace(regexNestedStackNames,
            (_, root, nested) => `${root}-${nestedStackNames[nested]}.template`
        );

        stack.Properties.TemplateURL = {
            "Fn::Join": [
                "", [
                    "https://",
                    {
                        "Fn::FindInMap": [
                            "SourceCode",
                            "General",
                            "GlobalS3Bucket"
                        ]
                    },
                    ".s3.",
                    { Ref: "AWS::URLSuffix" },
                    "/",
                    {
                        "Fn::FindInMap": [
                            "SourceCode",
                            "General",
                            "TemplateKeyPrefix"
                        ]
                    },
                    `/${name}`
                ]
            ]
        };
    });

    // Clean out each default IAM policy generated by CDK but never referenced
    const defaultPolicies = Object.entries(resources).filter(
        ([key, value]) => value.Type === "AWS::IAM::Policy" && /DefaultPolicy$/.test(key)
    ).map(([key, _]) => key);

    defaultPolicies.forEach(function (key) {
        // Delete the policy
        delete resources[key];
        // A role claims to depend on the policy even though it doesn't.
        // Remove the policy from the DependsOn list.
        const depends = Object.values(resources).filter(
            (value) => (value.DependsOn || []).includes(key)
        );
        depends.forEach(function (dependent) {
            const dependsOn = dependent.DependsOn;
            if (dependsOn.length === 1) {
                delete dependent.DependsOn;
            } else {
                dependent.DependsOn = dependsOn.filter((d) => d !== key);
            }
        });

    });

    // Remove unnecessary CDK metadata
    const removeProperty = function removeProperty(obj, ...keys) {
        const key = keys.shift();
        if (obj.hasOwnProperty(key)) {
            const prop = obj[key];
            if (keys.length) {
                if (removeProperty(prop, ...keys) && Object.keys(prop).length === 0) {
                    delete obj[key];
                    return true;
                }
            } else {
                delete obj[key];
                return true;
            }
        }
        return false;
    };

    const cdkMetadata = Object.values(resources).filter(
        (value) => Object.keys(value.Metadata || {}).some(
            (key) => key.startsWith("aws:")
        )
    );

    cdkMetadata.forEach(function (resource) {
        const keys = Object.keys(resource.Metadata);
        const rmKeys = keys.filter((key) => /^aws:(cdk|asset):(path|property)$/.test(key));
        if (keys.length === rmKeys.length) {
            delete resource.Metadata;
        } else {
            rmKeys.forEach((key) => delete resource.Metadata[key]);
        }
    });

    // Clean out CDK Metadata and condition it depends on
    removeProperty(resources, 'CDKMetadata');
    removeProperty(template, 'Conditions', 'CDKMetadataAvailable');

    // Clean-up BootstrapVersion parameter
    removeProperty(template, 'Parameters', 'BootstrapVersion');

    // Clean-up CheckBootstrapVersion Rule
    removeProperty(template, 'Rules', 'CheckBootstrapVersion');


    // Output modified template file
    const output_template = JSON.stringify(template, null, 2);
    fs.writeFileSync(`${global_s3_assets}/${file}`, output_template);
});
