/* eslint-disable perfectionist/sort-union-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable unicorn/no-array-for-each */
/* eslint-disable unicorn/prefer-node-protocol */
/* eslint-disable n/no-process-exit */
/* eslint-disable unicorn/no-process-exit */
/* eslint-disable perfectionist/sort-objects */
/* eslint-disable perfectionist/sort-imports */
/* eslint-disable padding-line-between-statements */
import { Command } from '@oclif/core';
import promptGitHubInstall from "../github-app-installer/installer.js";
import Conf from 'conf';
import api from '../aws/index.js';
import prompts from '../prompts/index.js';
import paths from '../util/paths.js';
import waitForState from '../util/wait.js';
import fs from 'fs';
import log from '../util/log.js';
import { v4 as uuidv4 } from "uuid";
import { formatDomainValidationOptions } from "../util/format.js";
const DEFAULT_NAME = 'campfire';
const S3_BUCKET_NAME = `campfire-previews-${uuidv4()}`;
const config = new Conf({ projectName: DEFAULT_NAME });
const clientsObj = api.clients;
function isCertificateDetail(obj) {
    return obj !== undefined;
}
function isDomainValidationOptions(obj) {
    return obj !== undefined;
}
async function generateAWSCertificate() {
    const domainName = config.get('USER_DOMAIN');
    clientsObj.acm = await api.initializeACMClient(config.get('AWS_REGION'));
    const { CertificateArn } = await api.requestSSLCertificate(domainName);
    config.set("CERTIFICATE_ARN", CertificateArn);
    await waitForState({
        startMsg: '...\n',
        successMsg: 'Pending AWS SSL Certificate has been generated\n',
        desiredState: true,
        describeFn: api.describeCertificate,
        describeArgs: CertificateArn,
        resCallback(response) {
            if (isCertificateDetail(response.Certificate)) {
                const certificate = response.Certificate;
                if (isDomainValidationOptions(certificate.DomainValidationOptions)) {
                    const domainValidationOptions = certificate.DomainValidationOptions;
                    return Boolean(domainValidationOptions[0].ResourceRecord);
                }
            }
            return false;
        },
    });
    const response = await api.describeCertificate(CertificateArn);
    const resourceRecords = response.Certificate.DomainValidationOptions.map((option) => ({
        name: option.ResourceRecord?.Name,
        value: option.ResourceRecord?.Value
    }));
    await log.text('Please add the following CNAME records to your domain to verify your SSL certificate.');
    log.note('NOTE: CloudFormation stack cannot be created until the certificate is verified via DNS.\n');
    log.info(formatDomainValidationOptions(resourceRecords, config.get("USER_DOMAIN")));
}
async function initialConfiguration() {
    if (config.size > 0) {
        log.warn('There is already existing Campfire infrastructure.\n');
        log.error('Please destroy first using `campfire destroy`.\n');
        process.exit(0);
    }
    const initialConfig = await prompts.welcome();
    config.set('AWS_REGION', initialConfig.awsRegion);
    config.set('USER_DOMAIN', initialConfig.userDomain);
    await log.text('');
    log.text('Your Campfire configuration file lives at ');
    log.info(config.path + "\n");
}
async function awsConfiguration() {
    await log.header('\nBuilding your Campfire infrastructure...\n');
    api.clients.cloudFormation = await api.initializeCfClient(config.get('AWS_REGION'));
    api.clients.s3 = await api.initializeS3Client(config.get('AWS_REGION'));
    config.set("S3_BUCKET_NAME", S3_BUCKET_NAME);
    await api.createS3Bucket(config.get("S3_BUCKET_NAME"));
    await api.uploadAllLambdas(config.get("S3_BUCKET_NAME"));
    await api.createStack({
        StackName: DEFAULT_NAME,
        TemplateBody: fs.readFileSync(paths.cloudFormationTemplatePath),
        Parameters: [
            {
                ParameterKey: "DefaultName",
                ParameterValue: DEFAULT_NAME,
            },
            {
                ParameterKey: "UserDomain",
                ParameterValue: config.get("USER_DOMAIN"),
            },
            {
                ParameterKey: "CertificateArn",
                ParameterValue: config.get("CERTIFICATE_ARN"),
            },
            {
                ParameterKey: "S3BucketName",
                ParameterValue: config.get("S3_BUCKET_NAME"),
            },
        ],
    });
    await waitForState({
        startMsg: 'Provisioning AWS resources\n',
        successMsg: 'AWS infrastructure provisioned successfully\n',
        desiredState: 'CREATE_COMPLETE',
        describeFn: api.getStackOutputs,
        describeArgs: { StackName: DEFAULT_NAME },
        resCallback(response) {
            return response.Stacks[0].StackStatus;
        },
    });
    const rawOutputs = await api.getStackOutputs({
        StackName: DEFAULT_NAME,
    });
    const outputs = {};
    rawOutputs.Stacks[0].Outputs.forEach((output) => {
        outputs[output.OutputKey] = output.OutputValue;
    });
    try {
        config.set('VPC_ID', outputs.VPCID);
        config.set('ALB_SECURITY_GROUP_ID', outputs.ALBSecurityGroupID);
        config.set('PREVIEW_CLUSTER_SUBNET_ID', outputs.PreviewClusterSubnetID);
        config.set('ALB_SUBNETA_ID', outputs.ALBSubnetAID);
        config.set('ALB_SUBNETB_ID', outputs.ALBSubnetBID);
        config.set('ROUTE_TABLE_ID', outputs.RouteTableId);
        config.set("EXECUTION_ROLE_ARN", outputs.ECSTaskExecutionRole);
        config.set('ALB_ARN', outputs.ALBARN);
        config.set('ALB_DOMAIN', outputs.ALBDomain);
        config.set('LISTENER_ARN', outputs.HTTPSListenerArn);
        config.set('DEFAULT_SUBNET_NAME', outputs.PreviewClusterSubnetID);
        config.set('SERVICE_SECURITY_GROUP', outputs.ServiceSecurityGroup);
        config.set("AWS_ACCESS_KEY_ID", outputs.AccessKeyId);
        config.set("AWS_SECRET_ACCESS_KEY", outputs.AccessKeySecret);
        config.set('APP_NAMES', '[]');
    }
    catch (error) {
        log.error(error);
    }
    log.text('   ');
    await log.header('Create two CNAME records for your custom domain\n');
    log.note("NOTE: Map '*.campfire-preview' and 'campfire' to this DNS Name:");
    log.info(`${outputs.ALBDomain}`);
    await log.text('\nIt may take around 10 minutes for AWS to fully spin up all infrastructure pieces.\n');
    log.header('Set up complete. See you at the campfire!');
    process.exit(1);
}
export default class Setup extends Command {
    static description = 'Initialize AWS infrastructure for Campfire';
    async run() {
        await initialConfiguration();
        await promptGitHubInstall();
        await generateAWSCertificate();
        await api.waitForCertificateValidated(config.get("CERTIFICATE_ARN"));
        await awsConfiguration();
    }
}
