"use strict";
var __asyncValues = (this && this.__asyncValues) || function (o) {
    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
    var m = o[Symbol.asyncIterator], i;
    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.S3Provider = void 0;
const client_s3_1 = require("@aws-sdk/client-s3");
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
const common_1 = require("@nestjs/common");
const fs_1 = require("fs");
const path_1 = require("path");
const abstract_provider_1 = require("./abstract.provider");
class S3Provider extends abstract_provider_1.AbstractProvider {
    constructor(setting) {
        super();
        this.setting = setting;
        this.initValidation();
    }
    resolveS3Key(filepath) {
        if (!filepath) {
            throw new Error('Invalid filepath for S3');
        }
        try {
            const url = new URL(filepath);
            const keyFromUrl = url.pathname.split('/').filter(Boolean).join('/');
            if (!keyFromUrl) {
                throw new Error(`Invalid filepath "${filepath}" for S3`);
            }
            return keyFromUrl;
        }
        catch (_a) {
            const keyFromPath = filepath.split('/').filter(Boolean).join('/');
            if (!keyFromPath) {
                throw new Error(`Invalid filepath "${filepath}" for S3`);
            }
            return keyFromPath;
        }
    }
    async buffer(filepath) {
        var _a, e_1, _b, _c;
        const s3 = await this.getClient();
        const key = this.resolveS3Key(filepath);
        const command = new client_s3_1.GetObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
        });
        const result = await s3.send(command);
        // @ts-ignore
        const stream = result.Body;
        const chunks = [];
        try {
            for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = await stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
                _c = stream_1_1.value;
                _d = false;
                const chunk = _c;
                chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
            }
        }
        catch (e_1_1) { e_1 = { error: e_1_1 }; }
        finally {
            try {
                if (!_d && !_a && (_b = stream_1.return)) await _b.call(stream_1);
            }
            finally { if (e_1) throw e_1.error; }
        }
        return Buffer.concat(chunks);
    }
    async initValidation() {
        if (!this.setting['storage-s3-key']) {
            throw new common_1.BadRequestException(`You must set the storage-s3-key in the setting.`);
        }
        if (!this.setting['storage-s3-secret']) {
            throw new common_1.BadRequestException(`You must set the storage-s3-secret in the setting.`);
        }
        if (!this.setting['storage-s3-region']) {
            throw new common_1.BadRequestException(`You must set the storage-s3-region in the setting.`);
        }
        if (!this.setting['storage-s3-bucket']) {
            throw new common_1.BadRequestException(`You must set the storage-s3-bucket in the setting.`);
        }
        const s3 = await this.getClient();
        try {
            await s3.send(new client_s3_1.HeadBucketCommand({
                Bucket: this.setting['storage-s3-bucket'],
            }));
        }
        catch (error) {
            throw new common_1.BadRequestException(`The bucket "${this.setting['storage-s3-bucket']}" does not exist or you do not have access to it.`);
        }
    }
    async getClient() {
        if (this.S3) {
            return this.S3;
        }
        this.S3Client = new client_s3_1.S3Client({
            credentials: {
                accessKeyId: this.setting['storage-s3-key'],
                secretAccessKey: this.setting['storage-s3-secret'],
            },
            region: this.setting['storage-s3-region'],
        });
        return (this.S3 = new client_s3_1.S3({
            credentials: {
                accessKeyId: this.setting['storage-s3-key'],
                secretAccessKey: this.setting['storage-s3-secret'],
            },
            region: this.setting['storage-s3-region'],
        }));
    }
    async getS3Client() {
        if (this.S3Client) {
            return this.S3Client;
        }
        this.S3Client = new client_s3_1.S3Client({
            credentials: {
                accessKeyId: this.setting['storage-s3-key'],
                secretAccessKey: this.setting['storage-s3-secret'],
            },
            region: this.setting['storage-s3-region'],
        });
        return this.S3Client;
    }
    async uploadFromString(destination, filename, fileContent) {
        const s3 = await this.getClient();
        const storedFilename = this.getFilename(filename);
        const key = [destination, storedFilename].join('/');
        const command = new client_s3_1.PutObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
            Body: fileContent,
        });
        await s3.send(command);
        // S3 v3 não retorna Location diretamente, construa a URL:
        return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
    }
    async upload(destination, file) {
        const s3 = await this.getClient();
        const storedFilename = this.getFilename(file.originalname);
        const key = [destination, storedFilename].join('/');
        const command = new client_s3_1.PutObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
            Body: file.buffer,
        });
        await s3.send(command);
        return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
    }
    async uploadFromPath(destination, filePath, options) {
        var _a;
        const s3 = await this.getClient();
        const originalName = (_a = options === null || options === void 0 ? void 0 : options.filename) !== null && _a !== void 0 ? _a : (0, path_1.basename)(filePath);
        const storedFilename = this.getFilename(originalName);
        const key = [destination, storedFilename].join('/');
        const command = new client_s3_1.PutObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
            Body: (0, fs_1.createReadStream)(filePath),
            ContentType: options === null || options === void 0 ? void 0 : options.mimetype,
        });
        await s3.send(command);
        return `https://${this.setting['storage-s3-bucket']}.s3.${this.setting['storage-s3-region']}.amazonaws.com/${key}`;
    }
    async delete(filepath) {
        const s3 = await this.getClient();
        const key = this.resolveS3Key(filepath);
        await s3.send(new client_s3_1.DeleteObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
        }));
        return true;
    }
    async readStream(filepath) {
        const s3 = await this.getClient();
        const key = this.resolveS3Key(filepath);
        const command = new client_s3_1.GetObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
        });
        const result = await s3.send(command);
        // @ts-ignore
        return result.Body;
    }
    async metaData(filepath) {
        const s3 = await this.getClient();
        const key = this.resolveS3Key(filepath);
        const command = new client_s3_1.HeadObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
        });
        const result = await s3.send(command);
        return result; // returns metadata
    }
    async tempURL(filepath, expires = 3600) {
        const s3Client = await this.getS3Client();
        const key = this.resolveS3Key(filepath);
        const command = new client_s3_1.GetObjectCommand({
            Bucket: this.setting['storage-s3-bucket'],
            Key: key,
        });
        // Use S3Client instance for getSignedUrl
        return await (0, s3_request_presigner_1.getSignedUrl)(s3Client, command, { expiresIn: expires });
    }
}
exports.S3Provider = S3Provider;
//# sourceMappingURL=s3.provider.js.map