/* eslint-disable perfectionist/sort-imports */
/* eslint-disable perfectionist/sort-named-imports */
/* eslint-disable perfectionist/sort-objects */
import { CloudFormationClient, CreateStackCommand, DescribeStacksCommand, DeleteStackCommand, } from '@aws-sdk/client-cloudformation';
import { ECSClient, CreateClusterCommand, DeleteClusterCommand, } from '@aws-sdk/client-ecs';
import { S3Client, CreateBucketCommand } from "@aws-sdk/client-s3";
import { ACMClient, RequestCertificateCommand, DescribeCertificateCommand, waitUntilCertificateValidated } from "@aws-sdk/client-acm";
import { CreateSecretCommand, ListSecretsCommand, PutSecretValueCommand, SecretsManagerClient, DeleteSecretCommand, } from "@aws-sdk/client-secrets-manager";
import { Upload } from "@aws-sdk/lib-storage";
import fs from "node:fs";
import executeProcess from './executeProcess.js';
import executeUpload from "./executeUpload.js";
import log from "../util/log.js";
const __dirname = new URL('.', import.meta.url).pathname;
// =============================== Custom Type =================================
const clients = {
    cloudFormation: null,
    ecs: null,
    s3: null,
    acm: null,
    secrets: null,
};
// ======================= AWS Client Initialization ===========================
const initializeCfClient = async (region) => new CloudFormationClient({ region });
const initializeEcsClient = async (region) => new ECSClient({ region });
const initializeS3Client = async (region) => new S3Client({ region });
const initializeACMClient = async (region) => new ACMClient({ region });
const initializeSecretsClient = async (region) => new SecretsManagerClient({ region });
// =========================== AWS CloudFormation ==============================
const createStack = ({ StackName, TemplateBody, Parameters }) => executeProcess({
    startMsg: 'Initializing CloudFormation Stack creation\n',
    successMsg: 'CloudFormation Stack initialized\n',
    client: clients.cloudFormation,
    command: new CreateStackCommand({
        Capabilities: ['CAPABILITY_NAMED_IAM', 'CAPABILITY_IAM'],
        StackName,
        TemplateBody,
        Parameters,
    }),
});
const getStackOutputs = async ({ StackName }) => executeProcess({
    startMsg: 'Retrieving AWS resource information\n',
    successMsg: 'AWS resource information retrieved\n',
    client: clients.cloudFormation,
    command: new DescribeStacksCommand({
        StackName,
    }),
    noSpinner: true,
});
const deleteStack = async ({ StackName }) => executeProcess({
    startMsg: 'Initiating Campfire CloudFormation stack deletion\n',
    successMsg: 'Stack deleted - it will take a few minutes to delete all AWS resources\n',
    client: clients.cloudFormation,
    command: new DeleteStackCommand({
        StackName,
    }),
});
// ============================= AWS ECS Cluster ===============================
const createCluster = async ({ clusterName }) => executeProcess({
    startMsg: `Creating cluster ${clusterName}\n`,
    successMsg: `${clusterName} cluster successfully created\n`,
    client: clients.ecs,
    command: new CreateClusterCommand({
        clusterName,
    }),
});
const deleteCluster = async ({ cluster }) => executeProcess({
    startMsg: `Deleting cluster ${cluster}\n`,
    successMsg: `${cluster} cluster deleted\n`,
    client: clients.ecs,
    command: new DeleteClusterCommand({
        cluster,
    }),
});
// ============================== AWS S3 BUCKET ================================
const createS3Bucket = async (bucketName) => executeProcess({
    startMsg: `Creating S3 bucket ${bucketName}\n`,
    successMsg: `S3 bucket ${bucketName} successfully created\n`,
    client: clients.s3,
    command: new CreateBucketCommand({
        Bucket: bucketName,
    }),
});
// =============================== AWS LAMBDAS =================================
const uploadLambdaGetComments = async (Bucket) => executeUpload({
    client: clients.s3,
    command: new Upload({
        client: clients.s3,
        params: {
            Body: fs.createReadStream(__dirname + "/../../lambdas/getComments.zip"),
            Bucket,
            Key: 'getComments'
        }
    }),
    noSpinner: true,
});
const uploadLambdaPostComment = async (Bucket) => executeUpload({
    client: clients.s3,
    command: new Upload({
        client: clients.s3,
        params: {
            Body: fs.createReadStream(__dirname + "/../../lambdas/postComment.zip"),
            Bucket,
            Key: 'postComment',
        }
    }),
    noSpinner: true,
});
const uploadLambdaGetReplay = async (Bucket) => executeUpload({
    client: clients.s3,
    command: new Upload({
        client: clients.s3,
        params: {
            Body: fs.createReadStream(__dirname + "/../../lambdas/getReplay.zip"),
            Bucket,
            Key: 'getReplay',
        }
    }),
    noSpinner: true,
});
const uploadLambdaPostReplay = async (Bucket) => executeUpload({
    client: clients.s3,
    command: new Upload({
        client: clients.s3,
        params: {
            Body: fs.createReadStream(__dirname + "/../../lambdas/postReplay.zip"),
            Bucket,
            Key: 'postReplay',
        }
    }),
    noSpinner: true,
});
const uploadAllLambdas = async (bucketName) => {
    const spinner = log.spin(log.secondary("Uploading all Lambda functions to S3"));
    await Promise.all([
        uploadLambdaGetComments(bucketName),
        uploadLambdaPostComment(bucketName),
        uploadLambdaGetReplay(bucketName),
        uploadLambdaPostReplay(bucketName),
    ]);
    spinner.succeed(log.success("Lambda functions successfully uploaded to S3"));
};
// ========================= AWS Certificate Manager ===========================
const requestSSLCertificate = async (DomainName) => executeProcess({
    startMsg: 'Requesting AWS SSL Certificate\n',
    successMsg: `AWS SSL Certificate request made\n`,
    client: clients.acm,
    command: new RequestCertificateCommand({
        DomainName,
        ValidationMethod: "DNS",
        SubjectAlternativeNames: [
            `*.${DomainName}`,
            `*.campfire-preview.${DomainName}`
        ]
    }),
});
const describeCertificate = async (CertificateArn) => executeProcess({
    client: clients.acm,
    command: new DescribeCertificateCommand({ CertificateArn }),
    noSpinner: true,
});
const waitForCertificateValidated = async (CertificateArn) => {
    await waitUntilCertificateValidated({
        client: clients.acm,
        maxWaitTime: 3000,
        minDelay: 1,
        maxDelay: 10,
    }, { CertificateArn });
};
// =========================== AWS Secrets Manager =============================
async function campfireSecretsExist() {
    const command = new ListSecretsCommand({
        IncludePlannedDeletion: false,
        Filters: [
            {
                Key: "name",
                Values: ["campfire/secrets/"],
            },
        ]
    });
    const { SecretList } = await clients.secrets.send(command);
    return SecretList.length > 0;
}
function storeSecretsGitHubAppInfo(body, secretsExist) {
    let command;
    const params = {
        "SecretString": JSON.stringify({
            APP_ID: body.id,
            OWNER: body.owner.login,
            CLIENT_ID: body.client_id,
            CLIENT_SECRET: body.client_secret,
        })
    };
    if (secretsExist) {
        params.SecretId = "campfire/secrets/github-app-secrets";
        command = new PutSecretValueCommand(params);
    }
    else {
        params.Description = "GitHub App's app id, owner, client id, and client secret";
        params.Name = "campfire/secrets/github-app-secrets";
        command = new CreateSecretCommand(params);
    }
    return command;
}
function storeSecretsGitHubAppPrivateKey(body, secretsExist) {
    let command;
    const params = {
        "SecretString": body.pem,
    };
    if (secretsExist) {
        params.SecretId = "campfire/secrets/private-key";
        command = new PutSecretValueCommand(params);
    }
    else {
        params.Name = "campfire/secrets/private-key";
        params.Description = "GitHub App's private key";
        command = new CreateSecretCommand(params);
    }
    return command;
}
async function createSecrets(body) {
    const secretsExist = await campfireSecretsExist();
    const command1 = storeSecretsGitHubAppInfo(body, secretsExist);
    const command2 = storeSecretsGitHubAppPrivateKey(body, secretsExist);
    await Promise.all([
        clients.secrets.send(command1),
        clients.secrets.send(command2)
    ]);
}
const destroySecrets = async () => {
    try {
        const command1 = new DeleteSecretCommand({
            SecretId: "campfire/secrets/github-app-secrets",
            RecoveryWindowInDays: 7,
        });
        const command2 = new DeleteSecretCommand({
            SecretId: "campfire/secrets/private-key",
            RecoveryWindowInDays: 7,
        });
        await Promise.all([
            clients.secrets.send(command1),
            clients.secrets.send(command2)
        ]);
    }
    catch (error) {
        console.log(`Secrets could not be deleted. \n${error.message || ""}`);
    }
};
export default {
    clients,
    initializeCfClient,
    initializeEcsClient,
    initializeS3Client,
    initializeACMClient,
    initializeSecretsClient,
    createStack,
    getStackOutputs,
    deleteStack,
    createCluster,
    deleteCluster,
    createS3Bucket,
    uploadAllLambdas,
    requestSSLCertificate,
    describeCertificate,
    waitForCertificateValidated,
    createSecrets,
    destroySecrets,
};
