"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get BambuMqttAdapter () {
        return BambuMqttAdapter;
    },
    get bambuEvent () {
        return bambuEvent;
    }
});
const _mqtt = /*#__PURE__*/ _interop_require_default(require("mqtt"));
const _socketstatetype = require("../../shared/dtos/socket-state.type");
const _apistatetype = require("../../shared/dtos/api-state.type");
const _printerapiinterface = require("../printer-api.interface");
const _octoprintwebsocketadapter = require("../octoprint/octoprint-websocket.adapter");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const bambuEvent = (event)=>`bambu.${event}`;
class BambuMqttAdapter {
    logger;
    settingsStore;
    eventEmitter2;
    printerType = _printerapiinterface.BambuType;
    printerId;
    socketState = _socketstatetype.SOCKET_STATE.unopened;
    apiState = _apistatetype.API_STATE.unset;
    login;
    lastMessageReceivedTimestamp = null;
    mqttClient = null;
    host = null;
    accessCode = null;
    serial = null;
    lastState = null;
    isConnecting = false;
    eventsAllowed = true;
    sequenceIdCounter = 10;
    isFirstMessage = true;
    constructor(settingsStore, loggerFactory, eventEmitter2){
        this.settingsStore = settingsStore;
        this.eventEmitter2 = eventEmitter2;
        this.logger = loggerFactory(BambuMqttAdapter.name);
    }
    registerCredentials(socketLogin) {
        const { printerId, loginDto } = socketLogin;
        this.printerId = printerId;
        this.login = loginDto;
        this.host = loginDto.printerURL?.replace(/^https?:\/\//, "");
        this.accessCode = loginDto.password || null;
        this.serial = loginDto.username || null;
    }
    needsReopen() {
        const isApiOnline = this.apiState === _apistatetype.API_STATE.responding;
        return isApiOnline && (this.socketState === _socketstatetype.SOCKET_STATE.closed || this.socketState === _socketstatetype.SOCKET_STATE.error);
    }
    needsSetup() {
        return this.socketState === _socketstatetype.SOCKET_STATE.unopened;
    }
    needsReauth() {
        return false;
    }
    isClosedOrAborted() {
        return this.socketState === _socketstatetype.SOCKET_STATE.closed || this.socketState === _socketstatetype.SOCKET_STATE.aborted;
    }
    async reauthSession() {
        this.logger.debug("reauthSession called but not needed for Bambu");
    }
    open() {
        if (!this.host || !this.accessCode || !this.serial) {
            throw new Error("Cannot open connection: credentials not registered");
        }
        this.connect(this.host, this.accessCode, this.serial);
    }
    close() {
        this.disconnect().catch((err)=>{
            this.logger.error("Error during MQTT disconnect:", err);
        });
    }
    async setupSocketSession() {
        if (!this.host || !this.accessCode || !this.serial) {
            this.updateSocketState(_socketstatetype.SOCKET_STATE.aborted);
            this.updateApiState(_apistatetype.API_STATE.noResponse);
            throw new Error("Credentials not properly registered");
        }
        this.updateSocketState(_socketstatetype.SOCKET_STATE.opening);
        this.updateApiState(_apistatetype.API_STATE.responding);
    }
    allowEmittingEvents() {
        this.eventsAllowed = true;
    }
    disallowEmittingEvents() {
        this.eventsAllowed = false;
    }
    connect(host, accessCode, serial) {
        if (this.mqttClient?.connected) {
            this.logger.debug("MQTT already connected");
            this.updateSocketState(_socketstatetype.SOCKET_STATE.opened);
            return;
        }
        if (this.isConnecting) {
            this.logger.warn("Connection already in progress");
            return;
        }
        this.host = host;
        this.accessCode = accessCode;
        this.serial = serial;
        this.isConnecting = true;
        this.updateSocketState(_socketstatetype.SOCKET_STATE.opening);
        const mqttUrl = `mqtts://${host}:8883`;
        const timeout = this.settingsStore.getTimeoutSettings().apiTimeout;
        this.logger.log(`Connecting to Bambu MQTT at ${mqttUrl}`);
        const connectionTimeout = setTimeout(()=>{
            this.isConnecting = false;
            this.updateSocketState(_socketstatetype.SOCKET_STATE.error);
            this.logger.error("MQTT connection timeout - will keep trying to reconnect");
        }, timeout);
        try {
            this.mqttClient = _mqtt.default.connect(mqttUrl, {
                username: "bblp",
                password: accessCode,
                reconnectPeriod: 5000,
                rejectUnauthorized: false
            });
            this.mqttClient.on("connect", ()=>{
                clearTimeout(connectionTimeout);
                this.isConnecting = false;
                this.updateSocketState(_socketstatetype.SOCKET_STATE.authenticated);
                this.updateApiState(_apistatetype.API_STATE.responding);
                this.logger.log("MQTT connected successfully");
                this.logger.debug(`Connected to MQTT broker at mqtts://${host}:8883`);
                const reportTopic = `device/${serial}/report`;
                this.mqttClient.subscribe(reportTopic, {
                    qos: 0
                }, (err)=>{
                    if (err) {
                        this.logger.error(`Failed to subscribe to ${reportTopic}:`, err);
                        this.updateSocketState(_socketstatetype.SOCKET_STATE.error);
                    } else {
                        this.logger.debug(`Subscribed to ${reportTopic}`);
                        this.sendPushallCommand().catch((err)=>{
                            this.logger.error("Failed to send pushall command:", err);
                        });
                    }
                });
            });
            this.mqttClient.on("error", (error)=>{
                this.isConnecting = false;
                this.updateSocketState(_socketstatetype.SOCKET_STATE.error);
                this.emitEvent(_octoprintwebsocketadapter.WsMessage.WS_ERROR, error.message).catch(()=>{});
                this.logger.error("MQTT error:", error);
            });
            this.mqttClient.on("message", (topic, message)=>{
                this.lastMessageReceivedTimestamp = Date.now();
                this.handleMessage(topic, message);
            });
            this.mqttClient.on("disconnect", ()=>{
                this.updateSocketState(_socketstatetype.SOCKET_STATE.closed);
                this.emitEvent(_octoprintwebsocketadapter.WsMessage.WS_CLOSED, "disconnected").catch(()=>{});
                this.logger.warn("MQTT disconnected");
            });
            this.mqttClient.on("reconnect", ()=>{
                this.updateSocketState(_socketstatetype.SOCKET_STATE.opening);
                this.logger.log("MQTT attempting to reconnect...");
                this.isFirstMessage = true;
            });
            this.mqttClient.on("close", ()=>{
                this.updateSocketState(_socketstatetype.SOCKET_STATE.closed);
                this.updateApiState(_apistatetype.API_STATE.noResponse);
                this.emitEvent(_octoprintwebsocketadapter.WsMessage.WS_CLOSED, "connection closed").catch(()=>{});
                this.logger.warn("MQTT connection closed - automatic reconnection will be attempted");
                if (this.lastState) {
                    const offlineMessage = this.transformStateToCurrentMessage(this.lastState);
                    this.emitEvent("current", {
                        ...offlineMessage,
                        print: this.lastState
                    }).catch(()=>{});
                }
            });
            this.mqttClient.on("offline", ()=>{
                this.updateSocketState(_socketstatetype.SOCKET_STATE.closed);
                this.updateApiState(_apistatetype.API_STATE.noResponse);
                this.logger.warn("MQTT client offline - automatic reconnection will be attempted");
                if (this.lastState) {
                    const offlineMessage = this.transformStateToCurrentMessage(this.lastState);
                    this.emitEvent("current", {
                        ...offlineMessage,
                        print: this.lastState
                    }).catch(()=>{});
                }
            });
        } catch (error) {
            clearTimeout(connectionTimeout);
            this.isConnecting = false;
            this.updateSocketState(_socketstatetype.SOCKET_STATE.error);
            this.logger.error("Failed to create MQTT client:", error);
            this.cleanup();
        }
    }
    async disconnect() {
        if (!this.mqttClient) {
            this.updateSocketState(_socketstatetype.SOCKET_STATE.closed);
            return;
        }
        this.logger.log("Disconnecting MQTT");
        this.updateSocketState(_socketstatetype.SOCKET_STATE.closed);
        return new Promise((resolve)=>{
            if (this.mqttClient?.connected) {
                this.mqttClient.end(false, {}, ()=>{
                    this.cleanup();
                    resolve();
                });
            } else {
                this.cleanup();
                resolve();
            }
        });
    }
    getLastState() {
        return this.lastState;
    }
    async sendPushallCommand() {
        const payload = {
            pushing: {
                sequence_id: this.sequenceIdCounter,
                command: "pushall",
                version: 1,
                push_target: 1
            }
        };
        this.logger.debug(`Sending command: ${JSON.stringify(payload)} with sequence ID: ${this.sequenceIdCounter}`);
        this.sequenceIdCounter++;
        await this.sendCommand(payload);
        this.logger.debug("Connected to printer via MQTT");
    }
    async sendCommand(payload) {
        if (!this.mqttClient?.connected) {
            throw new Error("MQTT not connected");
        }
        if (!this.serial) {
            throw new Error("Serial number not set");
        }
        const reportTopic = `device/${this.serial}/report`;
        const message = JSON.stringify(payload);
        return new Promise((resolve, reject)=>{
            this.mqttClient.publish(reportTopic, message, {
                qos: 0
            }, (err)=>{
                if (err) {
                    this.logger.error("Failed to send command:", err);
                    reject(err);
                } else {
                    this.logger.debug(`Command sent: ${message}`);
                    resolve();
                }
            });
        });
    }
    async startPrint(filename, subtask_name, amsMapping, plateNumber = 1) {
        await this.sendCommand({
            print: {
                command: "project_file",
                param: `Metadata/plate_${plateNumber}.gcode`,
                project_id: "0",
                profile_id: "0",
                task_id: "0",
                subtask_id: "0",
                subtask_name: subtask_name ?? filename,
                file: "",
                url: `file:///${filename}`,
                md5: "",
                timelapse: true,
                bed_type: "auto",
                bed_levelling: true,
                flow_cali: true,
                vibration_cali: true,
                layer_inspect: true,
                ams_mapping: amsMapping ? amsMapping.join(",") : "",
                use_ams: !!amsMapping && amsMapping.length > 0,
                sequence_id: this.sequenceIdCounter++
            }
        });
    }
    async pausePrint() {
        await this.sendCommand({
            print: {
                command: "pause",
                sequence_id: this.sequenceIdCounter++
            }
        });
    }
    async resumePrint() {
        await this.sendCommand({
            print: {
                command: "resume",
                sequence_id: this.sequenceIdCounter++
            }
        });
    }
    async stopPrint() {
        await this.sendCommand({
            print: {
                command: "stop",
                sequence_id: this.sequenceIdCounter++
            }
        });
    }
    async sendGcode(gcode) {
        await this.sendCommand({
            print: {
                command: "gcode_line",
                param: gcode,
                sequence_id: this.sequenceIdCounter++
            }
        });
    }
    resetSocketState() {
        this.lastState = null;
    }
    updateSocketState(state) {
        this.socketState = state;
        this.emitEventSync(_octoprintwebsocketadapter.WsMessage.WS_STATE_UPDATED, state);
    }
    updateApiState(state) {
        this.apiState = state;
        this.emitEventSync(_octoprintwebsocketadapter.WsMessage.API_STATE_UPDATED, state);
    }
    async emitEvent(event, payload) {
        if (!this.eventsAllowed) {
            return;
        }
        await this.eventEmitter2.emitAsync(bambuEvent(event), {
            event,
            payload,
            printerId: this.printerId,
            printerType: _printerapiinterface.BambuType
        });
    }
    emitEventSync(event, payload) {
        if (!this.eventsAllowed) {
            return;
        }
        this.eventEmitter2.emit(bambuEvent(event), {
            event,
            payload,
            printerId: this.printerId,
            printerType: _printerapiinterface.BambuType
        });
    }
    transformStateToCurrentMessage(state) {
        const isPrinting = state.gcode_state === "PRINTING" || state.mc_print_stage === "printing";
        const isPaused = state.mc_print_stage === "paused";
        const isConnected = this.mqttClient?.connected || false;
        const hasError = !isConnected || state.print_error !== 0;
        const isPausedText = isPaused ? "Paused" : "Printing";
        const onlineText = isPrinting ? isPausedText : "Operational";
        return {
            state: {
                text: isConnected ? onlineText : "Offline",
                flags: {
                    operational: isConnected,
                    printing: isConnected && isPrinting && !isPaused,
                    paused: isConnected && isPaused,
                    ready: isConnected && !isPrinting,
                    error: hasError,
                    cancelling: false,
                    pausing: false,
                    sdReady: isConnected,
                    closedOrError: !isConnected
                }
            },
            temps: [
                {
                    time: Date.now(),
                    tool0: {
                        actual: state.nozzle_temper || 0,
                        target: state.nozzle_target_temper || 0
                    },
                    bed: {
                        actual: state.bed_temper || 0,
                        target: state.bed_target_temper || 0
                    },
                    chamber: {
                        actual: state.chamber_temper || 0,
                        target: 0
                    }
                }
            ],
            progress: {
                completion: state.mc_percent || 0,
                printTime: null,
                printTimeLeft: state.mc_remaining_time ? state.mc_remaining_time * 60 : null
            },
            job: {
                file: {
                    name: state.gcode_file || state.subtask_name || null
                }
            },
            currentZ: state.layer_num || null,
            offsets: {},
            resends: {
                count: 0,
                transmitted: 0,
                ratio: 0
            },
            logs: [],
            messages: []
        };
    }
    handleMessage(topic, message) {
        try {
            const payload = JSON.parse(message.toString());
            if (topic.endsWith("/report") && payload.print) {
                this.lastState = payload.print;
                if (this.isFirstMessage) {
                    this.logger.debug("Initial message received");
                    this.isFirstMessage = false;
                }
                const currentMessage = this.transformStateToCurrentMessage(this.lastState);
                const combinedPayload = {
                    ...currentMessage,
                    print: payload.print
                };
                this.emitEvent("current", combinedPayload).catch((err)=>{
                    this.logger.error("Failed to emit current event:", err);
                });
            }
        } catch (error) {
            this.logger.error("Failed to parse MQTT message:", error);
        }
    }
    cleanup() {
        if (this.mqttClient) {
            this.mqttClient.removeAllListeners();
            this.mqttClient = null;
        }
        this.isConnecting = false;
        this.lastState = null;
    }
}

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