"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "BambuFtpAdapter", {
    enumerable: true,
    get: function() {
        return BambuFtpAdapter;
    }
});
const _basicftp = require("basic-ftp");
const _eventconstants = require("../../constants/event.constants");
const _nodefs = require("node:fs");
const _nodepath = require("node:path");
const _serverconstants = require("../../server.constants");
const _fsutils = require("../../utils/fs.utils");
class BambuFtpAdapter {
    settingsStore;
    eventEmitter2;
    logger;
    ftpClient = null;
    host = null;
    accessCode = null;
    isConnecting = false;
    constructor(settingsStore, loggerFactory, eventEmitter2){
        this.settingsStore = settingsStore;
        this.eventEmitter2 = eventEmitter2;
        this.settingsStore = settingsStore;
        this.eventEmitter2 = eventEmitter2;
        this.logger = loggerFactory(BambuFtpAdapter.name);
    }
    async connect(host, accessCode) {
        if (this.ftpClient && !this.ftpClient.closed) {
            this.logger.debug("FTP already connected");
            return;
        }
        if (this.isConnecting) {
            throw new Error("Connection already in progress");
        }
        const sanitizedHost = this.sanitizeHost(host);
        const sanitizedAccessCode = this.sanitizeAccessCode(accessCode);
        this.host = sanitizedHost;
        this.accessCode = sanitizedAccessCode;
        this.isConnecting = true;
        const timeout = this.settingsStore.getTimeoutSettings().apiTimeout;
        this.logger.log(`Connecting to Bambu FTP at ${sanitizedHost}:990`);
        try {
            this.ftpClient = new _basicftp.Client(timeout);
            this.ftpClient.ftp.verbose = false;
            await this.ftpClient.access({
                host: sanitizedHost,
                port: 990,
                user: "bblp",
                password: sanitizedAccessCode,
                secure: true,
                secureOptions: {
                    rejectUnauthorized: false,
                    minVersion: "TLSv1.2",
                    maxVersion: "TLSv1.3"
                }
            });
            this.isConnecting = false;
            this.logger.log("FTP connected successfully");
        } catch (error) {
            this.isConnecting = false;
            this.cleanup();
            this.logger.error("FTP connection failed:", error);
            throw error;
        }
    }
    async disconnect() {
        if (!this.ftpClient) {
            return;
        }
        this.logger.log("Disconnecting FTP");
        try {
            this.ftpClient.close();
        } catch (error) {
            this.logger.error("Error closing FTP:", error);
        } finally{
            this.cleanup();
        }
    }
    async listFiles(dirPath = "/") {
        this.ensureConnected();
        try {
            const files = await this.ftpClient.list(dirPath);
            this.logger.debug(`Listed ${files.length} files in ${dirPath}`);
            return files;
        } catch (error) {
            this.logger.error(`Failed to list files in ${dirPath}:`, error);
            throw error;
        }
    }
    getFileStoragePath(filename) {
        const storagePath = (0, _nodepath.join)((0, _fsutils.superRootPath)(), _serverconstants.AppConstants.defaultFileUploadsStorage);
        if (!(0, _nodefs.existsSync)(storagePath)) {
            (0, _nodefs.mkdirSync)(storagePath, {
                recursive: true
            });
        }
        return (0, _nodepath.join)(storagePath, filename);
    }
    async uploadFile(fileBuffer, filename, progressToken) {
        this.ensureConnected();
        const remotePath = `/sdcard/${filename}`;
        const tempPath = this.getFileStoragePath(`bambu-upload-${Date.now()}-${filename}`);
        try {
            (0, _nodefs.writeFileSync)(tempPath, fileBuffer);
            this.logger.debug(`Wrote temp file: ${tempPath}`);
            if (progressToken) {
                this.ftpClient.trackProgress((info)=>{
                    this.eventEmitter2.emit(`${(0, _eventconstants.uploadProgressEvent)(progressToken)}`, progressToken, {
                        loaded: info.bytes,
                        total: info.bytesOverall
                    });
                });
            }
            this.logger.log(`Uploading ${filename} to ${remotePath}`);
            await this.ftpClient.uploadFrom(tempPath, remotePath);
            this.ftpClient.trackProgress();
            if (progressToken) {
                this.eventEmitter2.emit(`${(0, _eventconstants.uploadDoneEvent)(progressToken)}`, progressToken);
            }
            this.logger.log(`File uploaded successfully: ${filename}`);
        } catch (error) {
            if (progressToken) {
                this.eventEmitter2.emit(`${(0, _eventconstants.uploadFailedEvent)(progressToken)}`, progressToken, error?.message);
            }
            this.logger.error(`Upload failed for ${filename}:`, error);
            throw error;
        } finally{
            try {
                (0, _nodefs.unlinkSync)(tempPath);
                this.logger.debug(`Cleaned up temp file: ${tempPath}`);
            } catch (cleanupError) {
                this.logger.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
            }
        }
    }
    async downloadFile(remotePath, localPath) {
        this.ensureConnected();
        try {
            this.logger.log(`Downloading ${remotePath} to ${localPath}`);
            await this.ftpClient.downloadTo(localPath, remotePath);
            this.logger.log(`File downloaded successfully: ${remotePath}`);
        } catch (error) {
            this.logger.error(`Download failed for ${remotePath}:`, error);
            throw error;
        }
    }
    async downloadFileAsStream(remotePath) {
        this.ensureConnected();
        const filename = remotePath.split("/").pop() || "download";
        const tempPath = this.getFileStoragePath(`bambu-download-${Date.now()}-${filename}`);
        try {
            this.logger.log(`Downloading ${remotePath} to temp file for streaming`);
            await this.ftpClient.downloadTo(tempPath, remotePath);
            this.logger.log(`File downloaded successfully: ${remotePath}`);
            const stream = (0, _nodefs.createReadStream)(tempPath);
            const cleanup = ()=>{
                try {
                    (0, _nodefs.unlinkSync)(tempPath);
                    this.logger.debug(`Cleaned up temp file: ${tempPath}`);
                } catch (cleanupError) {
                    this.logger.warn(`Failed to cleanup temp file ${tempPath}:`, cleanupError);
                }
            };
            stream.on("close", cleanup);
            stream.on("error", cleanup);
            return {
                stream,
                tempPath,
                cleanup
            };
        } catch (error) {
            try {
                (0, _nodefs.unlinkSync)(tempPath);
            } catch  {}
            this.logger.error(`Download failed for ${remotePath}:`, error);
            throw error;
        }
    }
    async deleteFile(remotePath) {
        this.ensureConnected();
        try {
            this.logger.log(`Deleting file: ${remotePath}`);
            await this.ftpClient.remove(remotePath);
            this.logger.log(`File deleted successfully: ${remotePath}`);
        } catch (error) {
            this.logger.error(`Delete failed for ${remotePath}:`, error);
            throw error;
        }
    }
    get isConnected() {
        return this.ftpClient != null && !this.ftpClient.closed;
    }
    ensureConnected() {
        if (!this.isConnected) {
            throw new Error("FTP not connected. Call connect() first.");
        }
    }
    cleanup() {
        this.ftpClient = null;
    }
    sanitizeHost(host) {
        if (!host?.length) {
            throw new Error("Host must be a non-empty string");
        }
        const trimmed = host.trim();
        if (trimmed.length === 0) {
            throw new Error("Host cannot be empty");
        }
        const ipv4Pattern = /^(\d{1,3}\.){3}\d{1,3}$/;
        const hostnamePattern = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
        if (!ipv4Pattern.test(trimmed) && !hostnamePattern.test(trimmed)) {
            throw new Error("Invalid host format. Must be a valid IP address or hostname");
        }
        if (ipv4Pattern.test(trimmed)) {
            const parts = trimmed.split(".").map(Number);
            if (parts.some((part)=>part < 0 || part > 255)) {
                throw new Error("Invalid IPv4 address. Octets must be between 0 and 255");
            }
        }
        return trimmed;
    }
    sanitizeAccessCode(accessCode) {
        if (!accessCode?.length) {
            throw new Error("Access code must be a non-empty string");
        }
        const trimmed = accessCode.trim();
        if (trimmed.length === 0) {
            throw new Error("Access code cannot be empty");
        }
        if (trimmed.length < 4 || trimmed.length > 32) {
            throw new Error("Access code must be between 4 and 32 characters");
        }
        const alphanumericPattern = /^[a-zA-Z0-9]+$/;
        if (!alphanumericPattern.test(trimmed)) {
            throw new Error("Access code must contain only alphanumeric characters");
        }
        return trimmed;
    }
}

//# sourceMappingURL=bambu-ftp.adapter.js.map