"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var S3FileService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.S3FileService = void 0;
const common_1 = require("@nestjs/common");
const client_s3_1 = require("@aws-sdk/client-s3");
const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner");
const error_messages_1 = require("../../constants/error-messages");
let S3FileService = S3FileService_1 = class S3FileService {
    constructor() {
        this.logger = new common_1.Logger(S3FileService_1.name);
        this.s3ClientCache = new Map();
        this.defaultRegion = null;
        this.defaultBucket = null;
        this.accessKey = null;
        this.secretKey = null;
    }
    onModuleInit() {
        this.initializeS3Credentials();
    }
    initializeS3Credentials() {
        this.accessKey = process.env.S3_AWS_ACCESS_KEY || null;
        this.secretKey = process.env.S3_AWS_SECRET_KEY || null;
        this.defaultRegion = process.env.S3_AWS_REGION_NAME || null;
        this.defaultBucket = process.env.S3_DEFAULT_BUCKET || null;
        if (!this.accessKey || !this.secretKey) {
            this.logger.warn('S3 credentials not fully configured. S3FileService will not be available.');
            return;
        }
        if (this.defaultRegion) {
            this.getOrCreateClient(this.defaultRegion);
            this.logger.log(`S3FileService initialized successfully with default region: ${this.defaultRegion}`);
        }
        else {
            this.logger.log('S3FileService initialized successfully (no default region, will use per-request region)');
        }
    }
    getOrCreateClient(region) {
        if (!this.accessKey || !this.secretKey) {
            throw new Error(error_messages_1.ERROR_MESSAGES.S3_CLIENT_NOT_INITIALIZED);
        }
        let client = this.s3ClientCache.get(region);
        if (!client) {
            client = new client_s3_1.S3Client({
                region,
                credentials: {
                    accessKeyId: this.accessKey,
                    secretAccessKey: this.secretKey,
                },
            });
            this.s3ClientCache.set(region, client);
            this.logger.debug(`Created S3 client for region: ${region}`);
        }
        return client;
    }
    getS3Client(region) {
        const effectiveRegion = region || this.defaultRegion;
        if (!effectiveRegion) {
            throw new Error('S3 region not specified and no default region configured. Please provide a region or set S3_AWS_REGION_NAME environment variable.');
        }
        return this.getOrCreateClient(effectiveRegion);
    }
    parsePath(path) {
        const colonIndex = path.indexOf(':');
        if (colonIndex > 0 && colonIndex < path.length - 1) {
            return {
                bucket: path.substring(0, colonIndex),
                key: path.substring(colonIndex + 1),
            };
        }
        if (this.defaultBucket) {
            return { bucket: this.defaultBucket, key: path };
        }
        throw new Error(`Invalid S3 path format: "${path}". Expected format: "bucket:key" or set S3_DEFAULT_BUCKET environment variable.`);
    }
    async read(path, options) {
        const s3Client = this.getS3Client(options?.region);
        const { bucket, key } = this.parsePath(path);
        const command = new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key });
        const response = await s3Client.send(command);
        const stream = response.Body;
        const chunks = [];
        for await (const chunk of stream) {
            chunks.push(Buffer.from(chunk));
        }
        return Buffer.concat(chunks);
    }
    async readStream(path, options) {
        const s3Client = this.getS3Client(options?.region);
        const { bucket, key } = this.parsePath(path);
        const command = new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key });
        const response = await s3Client.send(command);
        return response.Body;
    }
    async write(path, data, options) {
        const region = options?.region || this.defaultRegion;
        const s3Client = this.getS3Client(region);
        const { bucket, key } = this.parsePath(path);
        const command = new client_s3_1.PutObjectCommand({
            Bucket: bucket,
            Key: key,
            Body: typeof data === 'string' ? Buffer.from(data) : data,
            ContentType: options?.contentType,
        });
        await s3Client.send(command);
        this.logger.debug(`File uploaded to S3: ${bucket}/${key}`);
        return `https://${bucket}.s3.${region}.amazonaws.com/${key}`;
    }
    async writeStream(path, stream, options) {
        const region = options?.region || this.defaultRegion;
        const s3Client = this.getS3Client(region);
        const { bucket, key } = this.parsePath(path);
        const chunks = [];
        for await (const chunk of stream) {
            chunks.push(Buffer.from(chunk));
        }
        const buffer = Buffer.concat(chunks);
        const command = new client_s3_1.PutObjectCommand({
            Bucket: bucket,
            Key: key,
            Body: buffer,
            ContentType: options?.contentType,
        });
        await s3Client.send(command);
        this.logger.debug(`File uploaded to S3 via stream: ${bucket}/${key}`);
        return `https://${bucket}.s3.${region}.amazonaws.com/${key}`;
    }
    async delete(path, options) {
        const s3Client = this.getS3Client(options?.region);
        const { bucket, key } = this.parsePath(path);
        const command = new client_s3_1.DeleteObjectCommand({ Bucket: bucket, Key: key });
        await s3Client.send(command);
        this.logger.debug(`File deleted from S3: ${bucket}/${key}`);
    }
    async exists(path, options) {
        const s3Client = this.getS3Client(options?.region);
        const { bucket, key } = this.parsePath(path);
        try {
            const command = new client_s3_1.HeadObjectCommand({ Bucket: bucket, Key: key });
            await s3Client.send(command);
            return true;
        }
        catch (error) {
            if (error.name === 'NotFound' || error.$metadata?.httpStatusCode === 404) {
                return false;
            }
            throw error;
        }
    }
    async copy(sourcePath, destinationPath, options) {
        const s3Client = this.getS3Client(options?.region);
        if (options?.overwrite === false) {
            const destinationExists = await this.exists(destinationPath, { region: options?.region });
            if (destinationExists) {
                throw new Error(`Destination file already exists: ${destinationPath}`);
            }
        }
        const source = this.parsePath(sourcePath);
        const destination = this.parsePath(destinationPath);
        const command = new client_s3_1.CopyObjectCommand({
            Bucket: destination.bucket,
            Key: destination.key,
            CopySource: `${source.bucket}/${source.key}`,
            ContentType: options?.contentType,
        });
        await s3Client.send(command);
        this.logger.debug(`File copied in S3: ${source.bucket}/${source.key} -> ${destination.bucket}/${destination.key}`);
    }
    async getUrl(path, options) {
        const s3Client = this.getS3Client(options?.region);
        const { bucket, key } = this.parsePath(path);
        const command = new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key });
        const expiresIn = options?.expiresIn || 3600;
        return options.expiresIn > 0 ? (0, s3_request_presigner_1.getSignedUrl)(s3Client, command, { expiresIn }) : `https://${bucket}.s3.${options?.region || this.defaultRegion}.amazonaws.com/${key}`;
    }
};
exports.S3FileService = S3FileService;
exports.S3FileService = S3FileService = S3FileService_1 = __decorate([
    (0, common_1.Injectable)()
], S3FileService);
//# sourceMappingURL=s3-file.service.js.map