"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const execa_1 = tslib_1.__importDefault(require("execa"));
const errors_1 = tslib_1.__importDefault(require("@tradle/errors"));
const errors_2 = require("./errors");
const utils_1 = require("./utils");
const assertS3RestoreToolInstalled = () => {
    try {
        execa_1.default.sync('command', ['-v', 's3-pit-restore']);
    }
    catch (err) {
        throw new errors_2.Errors.NotFound(`please install the s3-pit-restore tool first. Here's how:

git clone https://github.com/tradle/s3-pit-restore
cd s3-pit-restore
python3 setup.py install
`);
    }
};
class S3 {
    constructor(client) {
        this.client = client;
        this.createBucket = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            S3.validateBucketName(bucket);
            try {
                yield this.client.createBucket({ Bucket: bucket }).promise();
            }
            catch (err) {
                if (errors_1.default.matches(err, { code: 'InvalidBucketName' })) {
                    throw new errors_2.Errors.InvalidInput(`invalid bucket name: ${bucket}`);
                }
                throw err;
            }
        });
        this.getBucketEncryption = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                const { ServerSideEncryptionConfiguration } = yield this.client.getBucketEncryption({ Bucket: bucket }).promise();
                return ServerSideEncryptionConfiguration;
            }
            catch (err) {
                errors_1.default.ignore(err, { code: 'ServerSideEncryptionConfigurationNotFoundError' });
                throw new errors_2.Errors.NotFound(`encryption configuration for bucket: ${bucket}`);
            }
        });
        this.setBucketEncryption = ({ bucket, encryption }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield this.client.putBucketEncryption({
                Bucket: bucket,
                ServerSideEncryptionConfiguration: encryption,
            }).promise();
        });
        this.getBucketVersioning = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { Status, MFADelete } = yield this.client.getBucketVersioning({ Bucket: bucket }).promise();
            return { Status, MFADelete };
        });
        this.setBucketVersioning = ({ bucket, versioning }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield this.client.putBucketVersioning({
                Bucket: bucket,
                VersioningConfiguration: versioning
            }).promise();
        });
        this.getBucketLifecycle = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                const { Rules = [] } = yield this.client.getBucketLifecycleConfiguration({ Bucket: bucket }).promise();
                return { Rules };
            }
            catch (err) {
                errors_1.default.ignore(err, { code: 'NoSuchLifecycleConfiguration' });
                throw new errors_2.Errors.NotFound(`lifecycle configuration for bucket: ${bucket}`);
            }
        });
        this.setBucketLifecycle = ({ bucket, lifecycle }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const params = {
                Bucket: bucket,
                LifecycleConfiguration: lifecycle,
            };
            yield this.client.putBucketLifecycleConfiguration(params).promise();
        });
        this.getBucketCORS = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                const { CORSRules = [] } = yield this.client.getBucketCors({ Bucket: bucket }).promise();
                return { CORSRules };
            }
            catch (err) {
                errors_1.default.ignore(err, { code: 'NoSuchCORSConfiguration' });
                throw new errors_2.Errors.NotFound(`CORS configuration for bucket: ${bucket}`);
            }
        });
        this.setBucketCORS = ({ bucket, cors }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const params = {
                Bucket: bucket,
                CORSConfiguration: cors,
            };
            yield this.client.putBucketCors(params).promise();
        });
        this.copyBucketSettings = ({ sourceName, destName }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield this.copyBucketEncryption({ sourceName, destName });
            yield this.copyBucketVersioning({ sourceName, destName });
            yield this.copyBucketLifecycle({ sourceName, destName });
            yield this.copyBucketCORS({ sourceName, destName });
        });
        this.copyBucketEncryption = ({ sourceName, destName }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            let encryption;
            try {
                encryption = yield this.getBucketEncryption(sourceName);
            }
            catch (err) {
                errors_1.default.ignore(err, errors_2.Errors.NotFound);
                return;
            }
            yield this.setBucketEncryption({ bucket: destName, encryption });
        });
        this.copyBucketVersioning = ({ sourceName, destName }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const versioning = yield this.getBucketVersioning(sourceName);
            if (versioning.Status) {
                yield this.setBucketVersioning({ bucket: destName, versioning });
            }
        });
        this.copyBucketLifecycle = ({ sourceName, destName }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            let lifecycle;
            try {
                lifecycle = yield this.getBucketLifecycle(sourceName);
            }
            catch (err) {
                errors_1.default.ignore(err, errors_2.Errors.NotFound);
                return;
            }
            yield this.setBucketLifecycle({ bucket: destName, lifecycle });
        });
        this.copyBucketCORS = ({ sourceName, destName }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            let cors;
            try {
                cors = yield this.getBucketCORS(sourceName);
            }
            catch (err) {
                errors_1.default.ignore(err, errors_2.Errors.NotFound);
                return;
            }
            yield this.setBucketCORS({ bucket: destName, cors });
        });
        this.assertBucketIsEmptyOrDoesNotExist = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const exists = yield this.doesBucketExist(bucket);
            if (exists) {
                yield this.assertBucketIsEmpty(bucket);
            }
            else {
                yield this.createBucket(bucket);
            }
        });
        this.assertBucketIsEmpty = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const isEmpty = yield this.isBucketEmpty(bucket);
            if (!isEmpty) {
                throw new errors_2.Errors.InvalidInput(`expected bucket to be empty: ${bucket}`);
            }
        });
        this.assertBucketExists = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const exists = yield this.doesBucketExist(bucket);
            if (!exists) {
                throw new errors_2.Errors.InvalidInput(`bucket does not exist: ${bucket}`);
            }
        });
        this.doesBucketExist = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                yield this.client.headBucket({ Bucket: bucket }).promise();
            }
            catch (err) {
                errors_1.default.ignore(err, { code: 'NotFound' });
                return false;
            }
            return true;
        });
        this.isBucketEmpty = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { Contents } = yield this.client.listObjectsV2({ Bucket: bucket, MaxKeys: 1 }).promise();
            return Contents.length === 0;
        });
        this.assertCanRestoreBucket = (opts) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { sourceName, destName, date } = opts;
            assertS3RestoreToolInstalled();
            utils_1.validateISODate(date);
            const exists = yield this.doesBucketExist(destName);
            if (exists) {
                yield this.assertBucketIsEmpty(destName);
            }
        });
        this.createIfNotExists = (bucket) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!(yield this.doesBucketExist(bucket))) {
                yield this.createBucket(bucket);
            }
        });
        this.restoreBucket = ({ sourceName, destName, date, profile }) => tslib_1.__awaiter(this, void 0, void 0, function* () {
            yield this.createIfNotExists(destName);
            yield this.copyBucketSettings({ sourceName, destName });
            const env = {};
            if (profile)
                env.AWS_PROFILE = profile;
            yield execa_1.default('s3-pit-restore', ['-b', sourceName, '-d', `s3://${destName}`, '-t', date], { env });
        });
    }
}
S3.validateBucketName = (name) => {
    if (name.length < 3 || name.length > 63) {
        throw new errors_2.Errors.InvalidInput(`bucket name must be between 3 and 63 characters long: ${name}`);
    }
};
exports.create = (client) => new S3(client);
//# sourceMappingURL=s3.js.map