"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "FileStorageService", {
    enumerable: true,
    get: function() {
        return FileStorageService;
    }
});
const _printjobentity = require("../entities/print-job.entity");
const _serverconstants = require("../server.constants");
const _fsutils = require("../utils/fs.utils");
const _nodepath = /*#__PURE__*/ _interop_require_wildcard(require("node:path"));
const _promises = require("node:fs/promises");
const _nodecrypto = require("node:crypto");
const _nodefs = require("node:fs");
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
class FileStorageService {
    printJobRepository;
    logger;
    storageBasePath;
    STORAGE_SUBDIRS = [
        "gcode",
        "3mf",
        "bgcode"
    ];
    constructor(loggerFactory, typeormService){
        this.printJobRepository = typeormService.getDataSource().getRepository(_printjobentity.PrintJob);
        this.logger = loggerFactory(FileStorageService.name);
        this.storageBasePath = (0, _nodepath.join)((0, _fsutils.superRootPath)(), _serverconstants.AppConstants.defaultPrintFilesStorage);
    }
    async ensureStorageDirectories() {
        try {
            await (0, _promises.mkdir)(this.storageBasePath, {
                recursive: true
            });
            for (const subdir of this.STORAGE_SUBDIRS){
                await (0, _promises.mkdir)((0, _nodepath.join)(this.storageBasePath, subdir), {
                    recursive: true
                });
            }
        } catch (error) {
            this.logger.error("Failed to create storage directories", error);
        }
    }
    readFile(fileStorageId) {
        const filePath = this.getFilePath(fileStorageId);
        return (0, _nodefs.readFileSync)(filePath);
    }
    async saveFile(file, fileHash) {
        const fileExt = (0, _nodepath.extname)(file.originalname).toLowerCase();
        let fileId;
        if (fileHash) {
            const nameHash = (0, _nodecrypto.createHash)('sha256').update(fileHash + file.originalname).digest('hex').substring(0, 32);
            fileId = `${nameHash.substring(0, 8)}-${nameHash.substring(8, 12)}-${nameHash.substring(12, 16)}-${nameHash.substring(16, 20)}-${nameHash.substring(20, 32)}`;
        } else {
            fileId = crypto.randomUUID();
        }
        let subdir = "gcode";
        if (fileExt === ".3mf" || file.originalname.includes(".gcode.3mf")) {
            subdir = "3mf";
        } else if (fileExt === ".bgcode") {
            subdir = "bgcode";
        }
        const targetDir = (0, _nodepath.join)(this.storageBasePath, subdir);
        const targetPath = (0, _nodepath.join)(targetDir, `${fileId}${fileExt}`);
        if (file.path) {
            await (0, _promises.rename)(file.path, targetPath);
        } else if (file.buffer) {
            await (0, _promises.writeFile)(targetPath, file.buffer);
        } else {
            throw new Error("File has no path or buffer");
        }
        this.logger.log(`Saved file ${file.originalname} as ${fileId}`);
        return fileId;
    }
    async getFile(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            throw new Error(`File ${fileStorageId} not found in storage`);
        }
        return (0, _promises.readFile)(filePath);
    }
    async deleteFile(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            this.logger.warn(`File ${fileStorageId} not found, cannot delete`);
            return;
        }
        await (0, _promises.unlink)(filePath);
        const metadataPath = filePath + ".json";
        try {
            await (0, _promises.unlink)(metadataPath);
            this.logger.debug(`Deleted metadata JSON for ${fileStorageId}`);
        } catch  {}
        const thumbnailDir = filePath.replace(/\.(gcode|3mf|bgcode)$/i, '_thumbnails');
        try {
            await (0, _promises.rm)(thumbnailDir, {
                recursive: true,
                force: true
            });
            this.logger.debug(`Deleted thumbnails for ${fileStorageId}`);
        } catch  {}
        this.logger.log(`Deleted file ${fileStorageId}`);
    }
    getFilePath(fileStorageId) {
        for (const subdir of this.STORAGE_SUBDIRS){
            for (const ext of [
                ".gcode",
                ".3mf",
                ".bgcode",
                ""
            ]){
                const fullPath = (0, _nodepath.join)(this.storageBasePath, subdir, fileStorageId + ext);
                if ((0, _nodefs.existsSync)(fullPath)) {
                    return fullPath;
                }
            }
        }
        return (0, _nodepath.join)(this.storageBasePath, "gcode", fileStorageId);
    }
    async calculateFileHash(filePath) {
        const fileBuffer = await (0, _promises.readFile)(filePath);
        const hashSum = (0, _nodecrypto.createHash)("sha256");
        hashSum.update(fileBuffer);
        return hashSum.digest("hex");
    }
    getDeterministicId(fileHash, fileName) {
        const nameHash = (0, _nodecrypto.createHash)('sha256').update(fileHash + fileName).digest('hex').substring(0, 32);
        return `${nameHash.substring(0, 8)}-${nameHash.substring(8, 12)}-${nameHash.substring(12, 16)}-${nameHash.substring(16, 20)}-${nameHash.substring(20, 32)}`;
    }
    async findFilePath(fileStorageId) {
        for (const subdir of this.STORAGE_SUBDIRS){
            const dirPath = (0, _nodepath.join)(this.storageBasePath, subdir);
            try {
                const files = await (0, _promises.readdir)(dirPath);
                const matchingFile = files.find((f)=>f.startsWith(fileStorageId));
                if (matchingFile) {
                    return (0, _nodepath.join)(dirPath, matchingFile);
                }
            } catch  {}
        }
        return null;
    }
    async fileExists(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        return filePath !== null;
    }
    async findDuplicateByHash(fileHash) {
        return this.printJobRepository.findOne({
            where: {
                fileHash
            },
            order: {
                createdAt: "DESC"
            }
        });
    }
    async saveMetadata(fileStorageId, metadata, fileHash, originalFileName, thumbnailMetadata) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            this.logger.warn(`Cannot save metadata - file ${fileStorageId} not found`);
            return;
        }
        const metadataPath = filePath + ".json";
        let existingOriginalFileName = originalFileName;
        let existingThumbnails = thumbnailMetadata;
        try {
            const existingContent = await (0, _promises.readFile)(metadataPath, "utf8");
            const existing = JSON.parse(existingContent);
            if (existing._originalFileName && !originalFileName) {
                existingOriginalFileName = existing._originalFileName;
            }
            if (existing._thumbnails && !thumbnailMetadata) {
                existingThumbnails = existing._thumbnails;
            }
        } catch  {}
        const metadataWithMeta = {
            ...metadata,
            _fileHash: fileHash || null,
            _analyzedAt: new Date().toISOString(),
            _fileStorageId: fileStorageId,
            _originalFileName: existingOriginalFileName || metadata.fileName || null,
            _thumbnails: existingThumbnails || []
        };
        await (0, _promises.writeFile)(metadataPath, JSON.stringify(metadataWithMeta, null, 2), "utf8");
        const thumbnailMeta = thumbnailMetadata ? ` with ${thumbnailMetadata.length} thumbnail(s)` : '';
        this.logger.debug(`Saved metadata for ${fileStorageId}${thumbnailMeta}`);
    }
    async loadMetadata(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            return null;
        }
        const metadataPath = filePath + ".json";
        try {
            const content = await (0, _promises.readFile)(metadataPath, "utf8");
            return JSON.parse(content);
        } catch (error) {
            return null;
        }
    }
    async hasMetadata(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            return false;
        }
        const metadataPath = filePath + ".json";
        try {
            await (0, _promises.access)(metadataPath);
            return true;
        } catch  {
            return false;
        }
    }
    async saveThumbnails(fileStorageId, thumbnails) {
        const savedThumbnails = [];
        if (!thumbnails || thumbnails.length === 0) {
            return savedThumbnails;
        }
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) {
            this.logger.warn(`Cannot save thumbnails - file ${fileStorageId} not found`);
            return savedThumbnails;
        }
        const thumbnailDir = filePath.replace(/\.(gcode|3mf|bgcode)$/i, '_thumbnails');
        try {
            await (0, _promises.rm)(thumbnailDir, {
                recursive: true,
                force: true
            });
            this.logger.debug(`Cleared old thumbnails for ${fileStorageId}`);
        } catch  {}
        await (0, _promises.mkdir)(thumbnailDir, {
            recursive: true
        });
        for(let i = 0; i < thumbnails.length; i++){
            const thumb = thumbnails[i];
            if (!thumb.data) continue;
            const ext = thumb.format?.toLowerCase() || 'png';
            const filename = `thumb_${i}.${ext}`;
            const thumbPath = (0, _nodepath.join)(thumbnailDir, filename);
            try {
                const buffer = Buffer.from(thumb.data, 'base64');
                await (0, _promises.writeFile)(thumbPath, buffer);
                const relativePath = _nodepath.default.relative(this.storageBasePath, thumbPath);
                savedThumbnails.push({
                    index: i,
                    path: relativePath,
                    filename,
                    width: thumb.width || 0,
                    height: thumb.height || 0,
                    format: ext,
                    size: buffer.length
                });
                this.logger.debug(`Saved thumbnail ${i} for ${fileStorageId} (${thumb.width}x${thumb.height}, ${buffer.length} bytes)`);
            } catch (error) {
                this.logger.warn(`Failed to save thumbnail ${i} for ${fileStorageId}: ${error}`);
            }
        }
        return savedThumbnails;
    }
    async getThumbnail(fileStorageId, index) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) return null;
        const thumbnailDir = filePath.replace(/\.(gcode|3mf|bgcode)$/i, '_thumbnails');
        for (const ext of [
            'png',
            'jpg',
            'jpeg',
            'qoi'
        ]){
            const thumbPath = (0, _nodepath.join)(thumbnailDir, `thumb_${index}.${ext}`);
            try {
                return await (0, _promises.readFile)(thumbPath);
            } catch  {}
        }
        return null;
    }
    async listThumbnails(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) return [];
        const thumbnailDir = filePath.replace(/\.(gcode|3mf|bgcode)$/i, '_thumbnails');
        try {
            const files = await (0, _promises.readdir)(thumbnailDir);
            return files.filter((f)=>f.startsWith('thumb_')).sort((a, b)=>a.localeCompare(b));
        } catch  {
            return [];
        }
    }
    async listAllFiles() {
        const files = [];
        for (const subdir of this.STORAGE_SUBDIRS){
            const dirPath = (0, _nodepath.join)(this.storageBasePath, subdir);
            try {
                const dirFiles = await (0, _promises.readdir)(dirPath);
                for (const file of dirFiles){
                    if (file.endsWith('_thumbnails') || file.endsWith('.json')) continue;
                    const fileId = _nodepath.default.parse(file).name;
                    const filePath = (0, _nodepath.join)(dirPath, file);
                    const stats = await (0, _promises.stat)(filePath);
                    const metadata = await this.loadMetadata(fileId);
                    const thumbnails = await this.listThumbnails(fileId);
                    files.push({
                        fileStorageId: fileId,
                        fileName: metadata?._fileName || file,
                        fileFormat: subdir,
                        fileSize: stats.size,
                        fileHash: metadata?._fileHash || '',
                        createdAt: stats.birthtime,
                        thumbnailCount: thumbnails.length,
                        metadata: metadata
                    });
                }
            } catch (error) {
                this.logger.error(`Error listing files in ${subdir}`, error);
            }
        }
        return files.sort((a, b)=>b.createdAt.getTime() - a.createdAt.getTime());
    }
    async getFileInfo(fileStorageId) {
        const filePath = await this.findFilePath(fileStorageId);
        if (!filePath) return null;
        try {
            const stats = await (0, _promises.stat)(filePath);
            const metadata = await this.loadMetadata(fileStorageId);
            const thumbnails = await this.listThumbnails(fileStorageId);
            const ext = (0, _nodepath.extname)(filePath).substring(1);
            return {
                fileStorageId,
                fileName: metadata?._originalFileName || (0, _nodepath.basename)(filePath),
                fileFormat: ext,
                fileSize: stats.size,
                fileHash: metadata?._fileHash || '',
                createdAt: stats.birthtime,
                thumbnailCount: thumbnails.length,
                metadata: metadata
            };
        } catch (error) {
            this.logger.error(`Error getting file info for ${fileStorageId}`, error);
            return null;
        }
    }
}

//# sourceMappingURL=file-storage.service.js.map