/**
 *  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 _regex = /[\w]*AssetParameters/g; //this regular express also takes into account lambda functions defined in nested stacks

// Paths
const global_s3_assets = "../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(`${global_s3_assets}/${file}`);
  let template = JSON.parse(raw_template);

  // Clean-up Lambda function code dependencies
  const resources = template.Resources ? template.Resources : {};
  const lambdaFunctions = Object.keys(resources).filter(function (key) {
    return resources[key].Type === "AWS::Lambda::Function";
  });
  lambdaFunctions.forEach(function (f) {
    const fn = template.Resources[f];
    if (fn.Properties.Code.hasOwnProperty("S3Bucket")) {
      // Set the S3 key reference
      let s3Key = Object.assign(fn.Properties.Code.S3Key);
      // https://github.com/aws/aws-cdk/issues/10608
      if (!s3Key.endsWith(".zip")) {
        fn.Properties.Code.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${s3Key}.zip`;
      } else {
        fn.Properties.Code.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${s3Key}`;
      }
      // Set the S3 bucket reference
      fn.Properties.Code.S3Bucket = {
        "Fn::Sub": "%%BUCKET_NAME%%-${AWS::Region}",
      };
    }
  });

  // Clean-up Lambda Layer code dependencies
  const lambdaLayers = Object.keys(resources).filter(function (key) {
    return resources[key].Type === "AWS::Lambda::LayerVersion";
  });
  lambdaLayers.forEach(function (l) {
    const layer = template.Resources[l];
    if (layer.Properties.Content.hasOwnProperty("S3Bucket")) {
      let s3Key = Object.assign(layer.Properties.Content.S3Key);
      layer.Properties.Content.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${s3Key}`;
      layer.Properties.Content.S3Bucket = {
        "Fn::Sub": "%%BUCKET_NAME%%-${AWS::Region}",
      };
    }
  });

  // Clean-up Custom::CDKBucketDeployment
  const bucketDeployments = Object.keys(resources).filter(function (key) {
    return resources[key].Type === "Custom::CDKBucketDeployment";
  });
  bucketDeployments.forEach(function (d) {
    const deployment = template.Resources[d];
    if (deployment.Properties.hasOwnProperty("SourceBucketNames")) {
      let s3Key = Object.assign(deployment.Properties.SourceObjectKeys[0]);
      deployment.Properties.SourceObjectKeys = [
        `%%SOLUTION_NAME%%/%%VERSION%%/${s3Key}`,
      ];
      deployment.Properties.SourceBucketNames = [
        {
          "Fn::Sub": "%%BUCKET_NAME%%-${AWS::Region}",
        },
      ];
    }
  });

  // Clean-up CustomCDKBucketDeployment Policy
  const bucketDeploymentsPolicy = Object.keys(resources).filter(function (key) {
    return (
      key.startsWith("CustomCDKBucketDeployment") &&
      resources[key].Type === "AWS::IAM::Policy"
    );
  });

  bucketDeploymentsPolicy.forEach(function (d) {
    const policy = template.Resources[d];
    let resources = policy.Properties.PolicyDocument.Statement[0].Resource;
    resources.forEach((res) => {
      res["Fn::Join"].forEach((key) => {
        if (key[2] == ":s3:::") {
          key[3]["Fn::Sub"] = "%%BUCKET_NAME%%-${AWS::Region}";
        }
      });
    });
  });

  // For the below code to work with nested templates, the nested template file has to
  // be added as metadata to the construct in the CDK code.
  //
  // const myNestedTemplate = new MyNestedStack(this, 'NestedStack', {
  //     parameters: {
  //         "FirstParameter": <some parameter 1>,
  //         "SecondParameter": <some parameter 2>,
  //         "SolutionID": solutionID,
  //         "SolutionName": solutionName,
  //         "ParentStackName": Aws.STACK_NAME
  //     }
  // });
  //
  // The slice at the end of the statement is to remove the ".json" extension. Since all templates are published ".template"
  // myNestedTemplate.nestedStackResource!.addMetadata('nestedStackFileName', myNestedTemplate.templateFile.slice(0, -(".json".length)));
  //
  // The below block of code has been tested to work with single nesting levels. This block of code has not been tested for multilevel
  // nesting of stacks
  // nestedStacks.forEach(function(f) {
  //     const fn = template.Resources[f];
  //     fn.Properties.TemplateURL = {
  //          'Fn::Join': [
  //             '',
  //             [
  //                 'https://%%TEMPLATE_BUCKET_NAME%%.s3.',
  //                 {
  //                     'Ref' : 'AWS::URLSuffix'
  //                 },
  //                 '/',
  //                 `%%SOLUTION_NAME%%/%%VERSION%%/${fn.Metadata.nestedStackFileName}`
  //             ]
  //          ]
  //     };

  //     const params = fn.Properties.Parameters ? fn.Properties.Parameters : {};
  //     const nestedStackParameters = Object.keys(params).filter(function(key) {
  //         if (key.search(_regex) > -1) {
  //             return true;
  //         }
  //         return false;
  //     });

  //     nestedStackParameters.forEach(function(stkParam) {
  //         fn.Properties.Parameters[stkParam] = undefined;
  //     })
  // });

  // Clean-up parameters section
  const parameters = template.Parameters ? template.Parameters : {};
  const assetParameters = Object.keys(parameters).filter(function (key) {
    if (key.search(_regex) > -1) {
      return true;
    }
    return false;
  });
  assetParameters.forEach(function (a) {
    template.Parameters[a] = undefined;
  });

  // Clean-up BootstrapVersion parameter
  if (parameters.hasOwnProperty("BootstrapVersion")) {
    parameters.BootstrapVersion = undefined;
  }

  // Clean-up CheckBootstrapVersion Rule
  const rules = template.Rules ? template.Rules : {};
  if (rules.hasOwnProperty("CheckBootstrapVersion")) {
    rules.CheckBootstrapVersion = undefined;
  }

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