"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.uploadAudioDataset = exports.uploadAudioDatasetMiddleware = void 0;
const drizzle_orm_1 = require("drizzle-orm");
const extract_zip_1 = __importDefault(require("extract-zip"));
const fs = __importStar(require("fs"));
const multer_1 = __importDefault(require("multer"));
const path = __importStar(require("path"));
const uuid_1 = require("uuid");
const constants_1 = require("../../../constants");
const database_1 = require("../../../database");
const tables_1 = require("../../../database/tables");
const logger_1 = __importDefault(require("../../../logger"));
const datasetVoiceTranscript_1 = require("../../services/datasetRows/datasetVoiceTranscript");
const uploadFilesToAzure_1 = require("../../../util/uploadFilesToAzure");
// Configure multer for file uploads
const storage = multer_1.default.diskStorage({
    destination: (req, file, cb) => {
        const tempDir = path.resolve('temp_audio');
        if (!fs.existsSync(tempDir)) {
            fs.mkdirSync(tempDir, { recursive: true });
        }
        cb(null, tempDir);
    },
    filename: (req, file, cb) => {
        cb(null, `${(0, uuid_1.v4)()}-${file.originalname}`);
    },
});
const upload = (0, multer_1.default)({
    storage,
    fileFilter: (req, file, cb) => {
        if (file.mimetype === 'application/zip' || file.originalname.endsWith('.zip')) {
            cb(null, true);
        }
        else {
            cb(new Error('Only ZIP files are allowed'));
        }
    },
    limits: {
        fileSize: 50 * 1024 * 1024, // 50MB limit
    },
});
// Multer middleware for handling file uploads
exports.uploadAudioDatasetMiddleware = upload.single('zipFile');
// Helper function for cleanup
const cleanupTempFiles = (extractPath, zipFilePath) => {
    try {
        // Clean up extracted files
        if (extractPath && fs.existsSync(extractPath)) {
            const cleanup = (dir) => {
                if (fs.existsSync(dir)) {
                    const files = fs.readdirSync(dir);
                    for (const file of files) {
                        const filePath = path.join(dir, file);
                        const stat = fs.statSync(filePath);
                        if (stat.isDirectory()) {
                            cleanup(filePath);
                        }
                        else {
                            fs.unlinkSync(filePath);
                        }
                    }
                    fs.rmdirSync(dir);
                }
            };
            cleanup(extractPath);
        }
        // Clean up zip file
        if (zipFilePath && fs.existsSync(zipFilePath)) {
            fs.unlinkSync(zipFilePath);
        }
    }
    catch (cleanupError) {
        logger_1.default.error(`Error during cleanup: ${cleanupError}`);
    }
};
// Main handler function
const uploadAudioDataset = async (req, res) => {
    let extractPath;
    let zipFilePath;
    try {
        const token = req.headers['x-access-token'] || req.headers.authorization;
        const { name, projectId, orgId, status, variable_name, datasetId, dataType } = req.body;
        const zipFile = req.file;
        if (!zipFile) {
            return res.status(400).json({ error: 'No zip file uploaded' });
        }
        zipFilePath = zipFile.path;
        logger_1.default.info(`Processing audio dataset ${datasetId ? 'update' : 'upload'}: ${zipFile.originalname}`);
        if (!name || !projectId || !orgId) {
            // Clean up zip file on validation error
            cleanupTempFiles(undefined, zipFilePath);
            return res.status(400).json({ error: 'Missing required fields' });
        }
        const db = await (0, database_1.getDb)();
        const tempDir = path.resolve('temp_audio');
        // Create temp directory if it doesn't exist
        if (!fs.existsSync(tempDir)) {
            fs.mkdirSync(tempDir, { recursive: true });
        }
        try {
            // Extract zip file using extract-zip
            extractPath = path.resolve(tempDir, (0, uuid_1.v4)());
            fs.mkdirSync(extractPath, { recursive: true });
            await (0, extract_zip_1.default)(zipFile.path, { dir: extractPath });
            logger_1.default.info(`Extracted zip file to: ${extractPath}`);
            // Find all supported audio files recursively (.wav, .mp3, .opus)
            const audioFiles = [];
            const findAudioFiles = (dir) => {
                const files = fs.readdirSync(dir);
                for (const file of files) {
                    const filePath = path.join(dir, file);
                    const stat = fs.statSync(filePath);
                    if (stat.isDirectory()) {
                        findAudioFiles(filePath);
                    }
                    else if ((0, constants_1.isAllowedDatasetAudioExtension)(path.extname(file))) {
                        audioFiles.push(filePath);
                    }
                }
            };
            findAudioFiles(extractPath);
            if (audioFiles.length === 0) {
                cleanupTempFiles(extractPath, zipFilePath);
                return res.status(400).json({
                    error: 'No supported audio files found in zip. Allowed: .wav, .mp3, .opus',
                });
            }
            const maxBytesPerAudio = (0, constants_1.getDatasetAudioMaxFileBytes)();
            const maxMbPerAudio = constants_1.DATASET_DATA_TYPE_CONFIG[constants_1.DATASET_UPLOAD_DATA_TYPE.AUDIO].maxSize;
            const oversizedPaths = [];
            for (const filePath of audioFiles) {
                let size = 0;
                try {
                    size = fs.statSync(filePath).size;
                }
                catch {
                    cleanupTempFiles(extractPath, zipFilePath);
                    return res.status(400).json({
                        error: `Could not read audio file: ${path.basename(filePath)}`,
                    });
                }
                if (size > maxBytesPerAudio) {
                    oversizedPaths.push(path.basename(filePath));
                }
            }
            if (oversizedPaths.length > 0) {
                cleanupTempFiles(extractPath, zipFilePath);
                return res.status(400).json({
                    error: `${oversizedPaths.length} audio file(s) exceed the ${maxMbPerAudio}MB per-file limit (${oversizedPaths.slice(0, 5).join(', ')}${oversizedPaths.length > 5 ? ', …' : ''}). Remove or split them and try again.`,
                });
            }
            const transcripts = [];
            for (const filePath of audioFiles) {
                transcripts.push(await (0, datasetVoiceTranscript_1.transcribeDatasetAudioFile)(filePath, orgId));
            }
            // Prepare files for upload
            const filesToUpload = audioFiles.map((filePath, index) => ({
                wavFilePath: filePath,
                fileName: `${index + 1}-${path.basename(filePath)}`,
            }));
            // Upload files in batches
            const batchSize = 10;
            const batches = [];
            for (let i = 0; i < filesToUpload.length; i += batchSize) {
                const batch = filesToUpload.slice(i, i + batchSize);
                batches.push((0, uploadFilesToAzure_1.uploadAudioFiles)(batch, token, `workspace/${orgId}/project/${projectId}/dataset/${datasetId}`));
            }
            // Wait for all batches to complete
            const batchResults = await Promise.all(batches);
            const urlMap = batchResults.reduce((acc, curr) => ({ ...acc, ...curr }), {});
            // Create dataset data with audio URLs
            const datasetData = audioFiles.map((filePath, index) => {
                const baseName = path.basename(filePath);
                const fallbackName = path.parse(baseName).name;
                const valueText = transcripts[index]?.trim() || fallbackName;
                return {
                    [variable_name]: {
                        type: 'voice',
                        value: valueText,
                        voiceUrl: urlMap[`${index + 1}-${baseName}`] || null,
                    },
                };
            });
            // Create or update dataset in database
            let resultDataset;
            if (datasetId) {
                // Update existing dataset
                const [updatedDataset] = await db
                    .update(tables_1.trusysDatasetsTable)
                    .set({
                    name,
                    projectId,
                    status: Number.parseInt(status),
                    type: constants_1.DATASET_TYPE.UPLOAD,
                    fields: [variable_name],
                    config: {
                        variable_name,
                        dataType: dataType ? Number.parseInt(dataType) : null,
                    },
                    updatedAt: new Date(),
                })
                    .where((0, drizzle_orm_1.eq)(tables_1.trusysDatasetsTable.id, datasetId))
                    .returning();
                if (!updatedDataset) {
                    // Clean up temp files when dataset not found
                    cleanupTempFiles(extractPath, zipFilePath);
                    res.status(404).json({ error: 'Dataset not found' });
                    return;
                }
                // Delete existing records and insert new ones
                await db.delete(tables_1.datasetRecordsTable).where((0, drizzle_orm_1.eq)(tables_1.datasetRecordsTable.datasetId, datasetId));
                if (datasetData.length > 0) {
                    const recordInserts = datasetData.map((record, index) => ({
                        id: (0, uuid_1.v4)(),
                        datasetId,
                        recordData: record,
                        recordIndex: index,
                    }));
                    await db.insert(tables_1.datasetRecordsTable).values(recordInserts);
                }
                resultDataset = updatedDataset;
            }
            else {
                // Create new dataset
                const newDatasetId = (0, uuid_1.v4)();
                const [createdDataset] = await db
                    .insert(tables_1.trusysDatasetsTable)
                    .values({
                    id: newDatasetId,
                    name,
                    projectId,
                    status: Number.parseInt(status),
                    type: constants_1.DATASET_TYPE.UPLOAD,
                    fields: [variable_name],
                    config: {
                        variable_name,
                        dataType: dataType ? Number.parseInt(dataType) : null,
                    },
                    createdAt: new Date(),
                    updatedAt: new Date(),
                })
                    .returning();
                // Insert records into datasetRecordsTable
                if (datasetData.length > 0) {
                    const recordInserts = datasetData.map((record, index) => ({
                        id: (0, uuid_1.v4)(),
                        datasetId: newDatasetId,
                        recordData: record,
                        recordIndex: index,
                    }));
                    await db.insert(tables_1.datasetRecordsTable).values(recordInserts);
                }
                resultDataset = createdDataset;
            }
            // Clean up temp files
            cleanupTempFiles(extractPath, zipFilePath);
            res.json({ data: resultDataset });
        }
        catch (error) {
            logger_1.default.error(`Error processing audio dataset: ${error}`);
            // Clean up temp files on error
            cleanupTempFiles(extractPath, zipFilePath);
            res.status(500).json({ error: 'Failed to process audio dataset' });
        }
    }
    catch (error) {
        logger_1.default.error(`Error uploading audio dataset: ${error}`);
        // Clean up temp files on error
        cleanupTempFiles(extractPath, zipFilePath);
        res.status(500).json({ error: 'Failed to upload audio dataset' });
    }
};
exports.uploadAudioDataset = uploadAudioDataset;
//# sourceMappingURL=upload-audio-dataset.js.map