"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const path_1 = tslib_1.__importDefault(require("path"));
const lodash_1 = tslib_1.__importDefault(require("lodash"));
const listr_1 = tslib_1.__importDefault(require("listr"));
const errors_1 = tslib_1.__importDefault(require("@tradle/errors"));
const prompts_1 = require("./prompts");
const constants_1 = require("./constants");
const logger_1 = require("./logger");
const utils = tslib_1.__importStar(require("./utils"));
const kyc_services_1 = require("./kyc-services");
const errors_2 = require("./errors");
exports.chooseDeleteVsRetain = (resources) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    const [retainedBuckets, retainedOther] = lodash_1.default.partition(resources, r => r.type === 'bucket');
    const del = [];
    const retain = constants_1.BIG_BUCKETS.slice();
    for (const item of retainedBuckets.concat(retainedOther)) {
        const { type, value } = item;
        if (yield prompts_1.confirm(`Delete ${type} ${value}?`, false)) {
            del.push(item);
        }
        else {
            retain.push(item.name);
        }
    }
    return {
        delete: del,
        retain,
    };
});
exports.deleteResources = ({ client, resources, profile, stackId }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    logger_1.logger.info(`I will now delete:

${resources.map(r => r.value).join('\n')}

I'll be deleting a few things in parallel, try not to get dizzy...`);
    const [buckets, other] = lodash_1.default.partition(resources, r => r.type === 'bucket');
    const promiseDeleteBuckets = buckets.length
        ? exports.deleteBuckets({ client, buckets, profile })
        : Promise.resolve();
    const promiseDeleteRest = other.length
        ? Promise.all(other.map((resource) => tslib_1.__awaiter(this, void 0, void 0, function* () { return exports.deleteResource({ client, resource }); })))
        : Promise.resolve([]);
    yield Promise.all([
        promiseDeleteBuckets,
        promiseDeleteRest
    ]);
});
exports.destroy = (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    const { client, stackId, profile } = opts;
    const { stackName } = utils.parseStackArn(stackId);
    const { cloudformation } = client;
    yield prompts_1.confirmOrAbort(`DESTROY REMOTE MYCLOUD ${stackName}?? There's no undo for this one!`, false);
    yield prompts_1.confirmOrAbort(`Are you REALLY REALLY sure you want to MURDER ${stackName}?`, false);
    let retained = yield utils.listOutputResources({ cloudformation, stackId });
    retained = retained.filter(r => r.name !== 'SourceDeploymentBucket');
    retained = lodash_1.default.sortBy(retained, 'type');
    const existence = yield Promise.all(retained.map(resource => utils.doesResourceExist({ client, resource })));
    retained = retained.filter((r, i) => existence[i]);
    let delResourcesToo;
    if (retained.length) {
        logger_1.logger.info('the following resources will be retained when the stack is deleted');
        logger_1.logger.info(retained.map(r => `${r.name}: ${r.value}`).join('\n'));
        delResourcesToo = yield prompts_1.confirm(`do you want me to delete them? If you say yes, I'll ask you about them one by one`, false);
    }
    const delVsRetain = delResourcesToo
        ? yield exports.chooseDeleteVsRetain(retained)
        : { delete: [], retain: retained.map(r => r.name) };
    const tryDelServicesStack = () => tslib_1.__awaiter(this, void 0, void 0, function* () {
        try {
            yield kyc_services_1.deleteCorrespondingServicesStack({ cloudformation, stackId });
        }
        catch (err) {
            errors_1.default.ignore(err, errors_2.Errors.NotFound);
        }
    });
    yield new listr_1.default([
        {
            title: 'deleting KYC services stack',
            task: tryDelServicesStack,
        },
        {
            title: 'disabling termination protection',
            task: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    yield utils.disableStackTerminationProtection({ cloudformation, stackId });
                }
                catch (err) {
                    errors_1.default.ignore(err, errors_2.Errors.NotFound);
                    ctx.stackNotFound = true;
                }
            })
        },
        {
            title: 'deleting primary stack',
            enabled: ctx => ctx.stackNotFound !== true,
            task: (ctx) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                const opts = {
                    cloudformation,
                    params: { StackName: stackId }
                };
                try {
                    yield utils.deleteStackAndWait(opts);
                }
                catch (err) {
                    if (errors_1.default.matches(err, errors_2.Errors.NotFound)) {
                        return;
                    }
                    if (!delVsRetain.retain.includes('ServerlessDeploymentBucket') &&
                        (yield utils.isV1Stack({ cloudformation, stackId }))) {
                        delVsRetain.retain.push('ServerlessDeploymentBucket');
                    }
                    opts.params.RetainResources = lodash_1.default.uniq(delVsRetain.retain);
                    yield utils.deleteStackAndWait(opts);
                }
            })
        },
    ]).run();
    if (!delVsRetain.delete.length)
        return;
    logger_1.logger.info('deleting resources you chose not to retain');
    yield exports.deleteResources({ resources: delVsRetain.delete, client, profile, stackId });
});
exports.deleteBuckets = ({ client, buckets, profile }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    const [big, small] = lodash_1.default.partition(buckets, ({ name }) => constants_1.BIG_BUCKETS.includes(name));
    yield Promise.all(small.map(({ value }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
        logger_1.logger.info(`emptying and deleting: ${value}`);
        yield utils.destroyBucket(client.s3, value);
    })));
    if (!big.length)
        return;
    const bigIds = big.map(b => b.value);
    yield Promise.all(bigIds.map((id) => tslib_1.__awaiter(this, void 0, void 0, function* () {
        try {
            yield utils.markBucketForDeletion(client.s3, id);
        }
        catch (err) {
            errors_1.default.ignore(err, errors_2.Errors.NotFound);
        }
    })));
    const cleanupScriptPath = path_1.default.relative(process.cwd(), exports.createCleanupBucketsScript({
        buckets: bigIds,
        profile
    }));
    logger_1.logger.info(`The following buckets are too large to delete directly:
${bigIds.join('\n')}

Instead, I've marked them for deletion by S3. They should be emptied within a day or so

After that you can delete them from the console or with this little script I created for you: ${cleanupScriptPath}
`);
});
exports.deleteResource = (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    try {
        yield doDeleteResource(opts);
    }
    catch (err) {
        errors_1.default.ignore(err, errors_2.Errors.NotFound);
    }
});
const doDeleteResource = ({ client, resource }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
    switch (resource.type) {
        case 'table':
            yield utils.deleteTable({ dynamodb: client.dynamodb, tableName: resource.value });
            break;
        case 'key':
            yield utils.deleteKey({ kms: client.kms, keyId: resource.value });
            break;
        case 'loggroup':
            yield utils.deleteLogGroup({ logs: client.logs, name: resource.value });
            break;
        case 'restapi':
            yield utils.deleteRestApi({ apigateway: client.apigateway, apiId: resource.value });
            break;
        default:
            logger_1.logger.warn(`don't know how to delete resource of type: ${resource.type}`);
            break;
    }
});
exports.createCleanupBucketsScript = ({ buckets, profile }) => {
    const cleanupScript = fs_1.default.existsSync(path_1.default.resolve(process.cwd(), 'cleanup-buckets.sh'))
        ? `cleanup-buckets-${Date.now()}.sh`
        : `cleanup-buckets.sh`;
    const delBuckets = buckets.map(name => getDeleteBucketLine({ name, profile }));
    const scriptBody = `
#!/bin/bash

${delBuckets.join('\n')}
`;
    const scriptPath = path_1.default.resolve(process.cwd(), cleanupScript);
    fs_1.default.writeFileSync(scriptPath, scriptBody);
    fs_1.default.chmodSync(scriptPath, '0755');
    return scriptPath;
};
const getDeleteBucketLine = ({ name, profile }) => {
    let line = 'aws ';
    if (profile) {
        line += `--profile ${profile} `;
    }
    line += `s3 rb "s3://${name}"`;
    return line;
};
//# sourceMappingURL=destroy.js.map