"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BackupService = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const client_s3_1 = require("@aws-sdk/client-s3");
const database_1 = __importDefault(require("../config/database"));
const logger_1 = require("../config/logger");
class BackupService {
    static s3Client = null;
    static bucketName = process.env.AWS_BUCKET_NAME || 'indian-books-worldwide-backups';
    static initialize() {
        const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
        const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
        const region = process.env.AWS_REGION || 'ap-south-1';
        if (accessKeyId && secretAccessKey) {
            this.s3Client = new client_s3_1.S3Client({
                region,
                credentials: {
                    accessKeyId,
                    secretAccessKey,
                },
            });
            logger_1.logger.info('Backup Service: S3 Client initialized successfully.');
        }
        else {
            logger_1.logger.warn('Backup Service: AWS credentials missing. Backups will be stored locally.');
        }
    }
    /**
     * 1. GENERATE SQL DATABASE DUMP
     */
    static async backupDatabase() {
        logger_1.logger.info('Backup Service: Starting database backup...');
        const backupDir = path_1.default.join(process.cwd(), 'backups');
        if (!fs_1.default.existsSync(backupDir)) {
            fs_1.default.mkdirSync(backupDir, { recursive: true });
        }
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const dbFileName = `db-backup-${timestamp}.sql`;
        const dbFilePath = path_1.default.join(backupDir, dbFileName);
        try {
            // Build a clean, portable SQL database schema table dump dynamically using prisma metadata queries.
            // This is extremely robust and avoids system spawn dependency blocks (like missing mysqldump binary).
            let sqlDump = `-- Indian Books Worldwide SQL Dump\n-- Timestamp: ${new Date().toISOString()}\n\n`;
            // Extract tables metadata dynamically from database
            const tables = await database_1.default.$queryRawUnsafe(`
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = DATABASE()
      `);
            for (const t of tables) {
                const tableName = t.TABLE_NAME || t.table_name;
                sqlDump += `\n-- Table: ${tableName}\n`;
                // Fetch schema create query
                const createQueryRes = await database_1.default.$queryRawUnsafe(`SHOW CREATE TABLE \`${tableName}\``);
                if (createQueryRes && createQueryRes[0]) {
                    const createTableSql = createQueryRes[0]['Create Table'] || createQueryRes[0]['create table'] || '';
                    sqlDump += `${createTableSql};\n\n`;
                }
                // Fetch table data rows
                const rows = await database_1.default.$queryRawUnsafe(`SELECT * FROM \`${tableName}\` LIMIT 5000`);
                if (rows.length > 0) {
                    sqlDump += `INSERT INTO \`${tableName}\` VALUES \n`;
                    const valueStrings = rows.map((r) => {
                        const values = Object.values(r).map((v) => {
                            if (v === null)
                                return 'NULL';
                            if (v instanceof Date)
                                return `'${v.toISOString().slice(0, 19).replace('T', ' ')}'`;
                            if (typeof v === 'object')
                                return `'${JSON.stringify(v).replace(/'/g, "''")}'`;
                            if (typeof v === 'string')
                                return `'${v.replace(/'/g, "''")}'`;
                            return v;
                        });
                        return `(${values.join(', ')})`;
                    });
                    sqlDump += valueStrings.join(',\n') + ';\n';
                }
            }
            fs_1.default.writeFileSync(dbFilePath, sqlDump, 'utf8');
            logger_1.logger.info(`Backup Service: Database backup file saved locally: ${dbFilePath}`);
            // Upload to AWS S3 if client is initialized
            await this.uploadToS3(dbFilePath, `database/${dbFileName}`);
            return dbFilePath;
        }
        catch (err) {
            logger_1.logger.error(`Backup Service: Database backup failed: ${err.message}`);
            throw err;
        }
    }
    /**
     * 2. BACKUP UPLOADED MEDIA FILES
     */
    static async backupMediaFiles() {
        logger_1.logger.info('Backup Service: Starting media files backup...');
        const uploadDir = path_1.default.join(process.cwd(), 'uploads');
        const backupDir = path_1.default.join(process.cwd(), 'backups');
        if (!fs_1.default.existsSync(uploadDir)) {
            logger_1.logger.warn('Backup Service: No uploads directory found to back up.');
            return '';
        }
        if (!fs_1.default.existsSync(backupDir)) {
            fs_1.default.mkdirSync(backupDir, { recursive: true });
        }
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const mediaFileName = `media-backup-${timestamp}.json`;
        const mediaFilePath = path_1.default.join(backupDir, mediaFileName);
        try {
            // Extract media lists / local folder files
            const files = fs_1.default.readdirSync(uploadDir);
            const manifest = files.map((file) => {
                const filePath = path_1.default.join(uploadDir, file);
                const stats = fs_1.default.statSync(filePath);
                return {
                    filename: file,
                    sizeBytes: stats.size,
                    createdAt: stats.mtime,
                };
            });
            fs_1.default.writeFileSync(mediaFilePath, JSON.stringify(manifest, null, 2), 'utf8');
            logger_1.logger.info(`Backup Service: Media manifests backup saved locally: ${mediaFilePath}`);
            // Upload manifest to S3
            await this.uploadToS3(mediaFilePath, `media/${mediaFileName}`);
            return mediaFilePath;
        }
        catch (err) {
            logger_1.logger.error(`Backup Service: Media backup failed: ${err.message}`);
            throw err;
        }
    }
    /**
     * 3. UPLOAD BACKUP FILE TO AWS S3 BUCKET
     */
    static async uploadToS3(localPath, s3Key) {
        if (!this.s3Client) {
            logger_1.logger.info('Backup Service: S3 upload skipped (No active S3 connection).');
            return false;
        }
        try {
            const fileStream = fs_1.default.createReadStream(localPath);
            const command = new client_s3_1.PutObjectCommand({
                Bucket: this.bucketName,
                Key: s3Key,
                Body: fileStream,
            });
            await this.s3Client.send(command);
            logger_1.logger.info(`Backup Service: Successfully uploaded backup file to S3: ${s3Key}`);
            return true;
        }
        catch (err) {
            logger_1.logger.error(`Backup Service: S3 upload failed for ${s3Key}: ${err.message}`);
            return false;
        }
    }
}
exports.BackupService = BackupService;
//# sourceMappingURL=backup.service.js.map