"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "PrusaLinkApi", {
    enumerable: true,
    get: function() {
        return PrusaLinkApi;
    }
});
const _printerapiinterface = require("../printer-api.interface");
const _prusalinkhttpclientbuilder = require("./utils/prusa-link-http-client.builder");
const _fs = require("fs");
const _eventconstants = require("../../constants/event.constants");
const _runtimeexceptions = require("../../exceptions/runtime.exceptions");
const defaultLog = {
    adapter: "prusa-link"
};
class PrusaLinkApi {
    eventEmitter2;
    httpClientFactory;
    settingsStore;
    printerLogin;
    logger;
    authHeader = null;
    constructor(loggerFactory, eventEmitter2, httpClientFactory, settingsStore, printerLogin){
        this.eventEmitter2 = eventEmitter2;
        this.httpClientFactory = httpClientFactory;
        this.settingsStore = settingsStore;
        this.printerLogin = printerLogin;
        this.logger = loggerFactory(PrusaLinkApi.name);
        this.logger.debug("Constructed api client", this.logMeta());
    }
    get type() {
        return _printerapiinterface.PrusaLinkType;
    }
    set login(login) {
        this.printerLogin = login;
    }
    get client() {
        return this.createClient();
    }
    async getVersion() {
        const response = await this.client.get("/api/version");
        return response.data.server;
    }
    async validateConnection() {
        await this.getVersion();
    }
    async getFiles() {
        const response = await this.client.get("/api/files");
        return response.data.files.filter((dir)=>dir.path === "/usb")[0].children.map((f)=>({
                path: f.display,
                date: null,
                size: -1
            }));
    }
    async getFile(path) {
        const response = await this.getFileRaw(path);
        return {
            path: response.data.name,
            size: response.data.size,
            date: null
        };
    }
    async getStatus() {
        const response = await this.client.get("/api/v1/status");
        return response.data;
    }
    async getPrinterState() {
        const response = await this.client.get("/api/printer");
        return response.data;
    }
    async getJobState() {
        const response = await this.client.get("/api/job");
        return response.data;
    }
    connect() {
        throw new Error("Method not implemented.");
    }
    disconnect() {
        throw new Error("Method not implemented.");
    }
    restartServer() {
        throw new Error("Method not implemented.");
    }
    restartHost() {
        throw new Error("Method not implemented.");
    }
    restartPrinterFirmware() {
        throw new Error("Method not implemented.");
    }
    async startPrint(path) {
        await this.client.post(`/api/v1/files/usb/${path}`);
    }
    async pausePrint() {
        const jobId = await this.getCurrentJobId();
        if (!jobId) {
            this.logger.warn("Job pause command did not complete, job or job id not set");
            return;
        }
        await this.client.put(`/api/v1/job/${jobId}/pause`);
    }
    async resumePrint() {
        const jobId = await this.getCurrentJobId();
        if (!jobId) {
            this.logger.warn("Job resume command did not complete, job or job id not set");
            return;
        }
        await this.client.put(`/api/v1/job/${jobId}/resume`);
    }
    async cancelPrint() {
        const jobId = await this.getCurrentJobId();
        if (!jobId) {
            this.logger.warn("Job cancel command did not complete, job or job id not set");
            return;
        }
        await this.client.delete(`/api/v1/job/${jobId}`);
    }
    quickStop() {
        throw new Error("Method not implemented.");
    }
    sendGcode(script) {
        throw new Error("Method not implemented.");
    }
    movePrintHead(amounts) {
        throw new Error("Method not implemented.");
    }
    homeAxes(axes) {
        throw new Error("Method not implemented.");
    }
    async downloadFile(path) {
        const fileReference = await this.getFileRaw(path);
        const pathUrl = fileReference.data.refs.download;
        return await this.client.get(pathUrl, {
            responseType: "stream"
        });
    }
    async getFileChunk(path, startBytes, endBytes) {
        const fileReference = await this.getFileRaw(path);
        const pathUrl = fileReference.data.refs.download;
        return await this.createClient((o)=>o.withHeaders({
                Range: `bytes=${startBytes}-${endBytes}`
            })).get(pathUrl);
    }
    async uploadFile(multerFileOrBuffer, startPrint, progressToken) {
        try {
            let fileBuffer;
            const filename = multerFileOrBuffer.originalname;
            if (Buffer.isBuffer(multerFileOrBuffer)) {
                this.logger.log("Using file directly from memory buffer for upload");
                fileBuffer = multerFileOrBuffer;
            } else {
                const filePath = multerFileOrBuffer.path;
                this.logger.log(`Reading file from disk for upload: ${filePath}`);
                fileBuffer = (0, _fs.readFileSync)(filePath);
            }
            const contentLength = fileBuffer.length;
            const response = await this.createClient((b)=>{
                b.withHeaders({
                    "Content-Length": contentLength.toString(),
                    Overwrite: "?1",
                    "Print-After-Upload": startPrint ? "?1" : "?0"
                }).withTimeout(this.settingsStore.getTimeoutSettings().apiUploadTimeout).withOnUploadProgress((p)=>{
                    if (progressToken) {
                        this.eventEmitter2.emit(`${(0, _eventconstants.uploadProgressEvent)(progressToken)}`, progressToken, p);
                    }
                });
            }).put(`/api/v1/files/usb/${encodeURIComponent(filename)}`, fileBuffer);
            if (progressToken) {
                this.eventEmitter2.emit(`${(0, _eventconstants.uploadDoneEvent)(progressToken)}`, progressToken);
            }
            return response.data;
        } catch (e) {
            if (progressToken) {
                this.eventEmitter2.emit(`${(0, _eventconstants.uploadFailedEvent)(progressToken)}`, progressToken, e?.message);
            }
            let data;
            try {
                data = JSON.parse(e.response?.body);
            } catch  {
                data = e.response?.body;
            }
            throw new _runtimeexceptions.ExternalServiceError({
                error: e.message,
                statusCode: e.response?.statusCode,
                data,
                success: false,
                stack: e.stack
            }, "Prusa-Link");
        }
    }
    async deleteFile(path) {
        await this.client.delete(`/api/v1/files/usb/${path}`);
    }
    async deleteFolder(path) {
        await this.client.delete(`/api/v1/files/usb/${path}`);
    }
    getSettings() {
        throw new Error("Method not implemented.");
    }
    getReprintState() {
        throw new Error("Method not implemented.");
    }
    getFileRaw(path) {
        return this.client.get(`/api/v1/files/usb/${path}`);
    }
    async getCurrentJobId() {
        const status = await this.getStatus();
        return status.job?.id;
    }
    createClient(buildFluentOptions) {
        const builder = new _prusalinkhttpclientbuilder.PrusaLinkHttpClientBuilder();
        return this.httpClientFactory.createClientWithBaseUrl(builder, this.printerLogin.printerURL, (b)=>{
            this.logger.debug("Building API client", this.logMeta());
            b.withDigestAuth(this.printerLogin.username, this.printerLogin.password, (error)=>{
                this.logger.error("Authentication error occurred", error);
            }, (error, attemptCount)=>{
                this.logger.log(`Authentication attempt count ${attemptCount} for method ${error.config?.method?.toUpperCase()} path ${error.config?.url}`, this.logMeta());
            }, (authHeader)=>{
                this.logger.debug("Authentication successful, saving auth header for later reuse", this.logMeta());
                this.authHeader = authHeader;
            });
            if (this.authHeader) {
                b.withAuthHeader(this.authHeader);
            }
            if (buildFluentOptions && typeof buildFluentOptions === "function") {
                buildFluentOptions(b);
            }
        });
    }
    logMeta() {
        return defaultLog;
    }
}

//# sourceMappingURL=prusa-link.api.js.map