"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 OctoPrintMessage () {
        return OctoPrintMessage;
    },
    get OctoprintWebsocketAdapter () {
        return OctoprintWebsocketAdapter;
    },
    get WsMessage () {
        return WsMessage;
    },
    get octoPrintEvent () {
        return octoPrintEvent;
    },
    get octoPrintWebsocketEvent () {
        return octoPrintWebsocketEvent;
    }
});
const _httpstatuscodesconstants = require("../../constants/http-status-codes.constants");
const _serverconstants = require("../../server.constants");
const _runtimeexceptions = require("../../exceptions/runtime.exceptions");
const _urlutils = require("../../utils/url.utils");
const _normalizeurl = require("../../utils/normalize-url");
const _websocketadapter = require("../../shared/websocket.adapter");
const _socketstatetype = require("../../shared/dtos/socket-state.type");
const _apistatetype = require("../../shared/dtos/api-state.type");
const _printerapiinterface = require("../printer-api.interface");
const WsMessage = {
    WS_OPENED: "WS_OPENED",
    WS_CLOSED: "WS_CLOSED",
    WS_ERROR: "WS_ERROR",
    API_STATE_UPDATED: "API_STATE_UPDATED",
    WS_STATE_UPDATED: "WS_STATE_UPDATED"
};
const OctoPrintMessage = {
    connected: "connected",
    reauthRequired: "reauthRequired",
    current: "current",
    history: "history",
    event: "event",
    plugin: "plugin",
    timelapse: "timelapse",
    slicingProgress: "slicingProgress"
};
const octoPrintWebsocketEvent = (printerId)=>`octoprint.${printerId}`;
const octoPrintEvent = (event)=>`octoprint.${event}`;
class OctoprintWebsocketAdapter extends _websocketadapter.WebsocketAdapter {
    octoprintClient;
    eventEmitter2;
    configService;
    printerType = 0;
    printerId;
    stateUpdated = false;
    stateUpdateTimestamp = null;
    socketState = _socketstatetype.SOCKET_STATE.unopened;
    apiStateUpdated = false;
    apiStateUpdateTimestamp = null;
    apiState = _apistatetype.API_STATE.unset;
    lastMessageReceivedTimestamp = null;
    reauthRequired = false;
    reauthRequiredTimestamp = null;
    login;
    socketURL;
    sessionDto;
    username;
    refreshPrinterCurrentInterval;
    constructor(loggerFactory, octoprintClient, eventEmitter2, configService){
        super(loggerFactory), this.octoprintClient = octoprintClient, this.eventEmitter2 = eventEmitter2, this.configService = configService;
        this.logger = loggerFactory(OctoprintWebsocketAdapter.name);
    }
    get _debugMode() {
        return this.configService.get(_serverconstants.AppConstants.debugSocketStatesKey, _serverconstants.AppConstants.defaultDebugSocketStates) === "true";
    }
    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 this.reauthRequired;
    }
    isClosedOrAborted() {
        return this.socketState === _socketstatetype.SOCKET_STATE.closed || this.socketState === _socketstatetype.SOCKET_STATE.aborted;
    }
    registerCredentials(socketLogin) {
        const { printerId, loginDto } = socketLogin;
        this.printerId = printerId;
        this.login = loginDto;
        const httpUrlString = (0, _normalizeurl.normalizeUrl)(this.login.printerURL);
        const httpUrl = new URL(httpUrlString);
        const httpUrlPath = httpUrl.pathname;
        const wsUrl = (0, _urlutils.httpToWsUrl)(httpUrlString);
        wsUrl.pathname = (httpUrlPath ?? "/") + "sockjs/websocket";
        this.socketURL = wsUrl;
    }
    open() {
        if (this.socket) {
            throw new Error(`Socket already exists by printerId, ignoring open request`);
        }
        super.open(this.socketURL);
    }
    close() {
        clearInterval(this.refreshPrinterCurrentInterval);
        super.close();
    }
    async sendThrottle(throttle = _serverconstants.AppConstants.defaultSocketThrottleRate) {
        return await this.sendMessage(JSON.stringify({
            throttle
        }));
    }
    async reauthSession() {
        this.logger.log("Sending reauthSession");
        await this.setupSocketSession();
        await this.sendAuth();
        this.resetReauthRequired();
    }
    async setupSocketSession() {
        this.resetSocketState();
        this.sessionDto = await this.octoprintClient.login(this.login).then((d)=>{
            const r = d.data;
            if (r.name === "_api") {
                this.setApiState("globalKey");
                this.setSocketState("aborted");
                throw new _runtimeexceptions.ExternalServiceError("Global API Key detected, aborting socket connection", "OctoPrint");
            } else if (r.needs?.group[0] === "guests") {
                this.logger.warn("Detected group guests in OctoPrint login response, marking as unauthorized");
                this.setApiState("authFail");
                this.setSocketState("aborted");
                throw new _runtimeexceptions.ExternalServiceError("Guest group detected, authentication failed, aborting socket connection", "OctoPrint");
            }
            this.setApiState("responding");
            this.setSocketState("opening");
            return r;
        }).catch((e)=>{
            this.setSocketState("aborted");
            if (e instanceof _runtimeexceptions.ExternalServiceError) {
                this.logger.warn(`Printer authorization error, apiState: ${this.apiState}`);
                throw e;
            } else {
                if (e?.response?.status === 403) {
                    this.setApiState("authFail");
                    this.setSocketState("aborted");
                    throw new _runtimeexceptions.ExternalServiceError(e, "OctoPrint");
                }
                this.logger.error(`Printer (${this.printerId}) network or transport error, marking it as unreachable; ${e}`);
                this.setApiState("noResponse");
            }
            throw e;
        });
        this.username = await this.octoprintClient.getAdminUserOrDefault(this.login).catch((e)=>{
            const status = e.response?.status;
            this.setApiState("authFail");
            this.setSocketState("aborted");
            if (status && [
                _httpstatuscodesconstants.HttpStatusCode.BAD_GATEWAY,
                _httpstatuscodesconstants.HttpStatusCode.NOT_IMPLEMENTED,
                _httpstatuscodesconstants.HttpStatusCode.SERVICE_UNAVAILABLE,
                _httpstatuscodesconstants.HttpStatusCode.GATEWAY_TIMEOUT
            ].includes(status)) {
                this.logger.error(`Detected a 501-504 error (${status}) probably OctoPrint has crashed or is restarting`);
            }
            throw e;
        });
        await this.updateCurrentStateSafely();
        this.logger.log(`Setting up printer current interval loop with 10 seconds interval`);
        if (this.refreshPrinterCurrentInterval) {
            clearInterval(this.refreshPrinterCurrentInterval);
        }
        this.refreshPrinterCurrentInterval = setInterval(async ()=>{
            await this.updateCurrentStateSafely();
        }, 10000);
    }
    setReauthRequired() {
        this.reauthRequired = true;
        this.reauthRequiredTimestamp = Date.now();
    }
    resetReauthRequired() {
        this.reauthRequired = false;
        this.reauthRequiredTimestamp = null;
    }
    resetSocketState() {
        this.setSocketState("unopened");
        this.setApiState("unset");
    }
    emitEventSync(event, payload) {
        if (!this.eventEmittingAllowed) {
            return;
        }
        this.eventEmitter2.emit(octoPrintEvent(event), {
            event,
            payload,
            printerId: this.printerId,
            printerType: _printerapiinterface.OctoprintType
        });
    }
    async afterOpened(_) {
        this.setSocketState("opened");
        await this.sendAuth();
        await this.sendThrottle(_serverconstants.AppConstants.defaultSocketThrottleRate);
    }
    async onMessage(message) {
        this.lastMessageReceivedTimestamp = Date.now();
        if (this.socketState !== _socketstatetype.SOCKET_STATE.authenticated) {
            this.setSocketState("authenticated");
        }
        const data = JSON.parse(message);
        const eventName = Object.keys(data)[0];
        const payload = data[eventName];
        if (this._debugMode) {
            this.logger.log(`RX Msg ${eventName} ${message.substring(0, 140)}...`);
        }
        if (eventName === OctoPrintMessage.reauthRequired) {
            this.logger.log("Received 'reauthRequired', acting on it");
            this.setReauthRequired();
        }
        await this.emitEvent(eventName, payload);
    }
    async afterClosed(event) {
        this.setSocketState("closed");
        delete this.socket;
        await this.emitEvent(WsMessage.WS_CLOSED, "connection closed");
    }
    async onError(error) {
        this.setSocketState("error");
        await this.emitEvent(WsMessage.WS_ERROR, error?.length ? error : "connection error");
    }
    async updateCurrentStateSafely() {
        try {
            const current = await this.octoprintClient.getPrinterCurrent(this.login, true);
            const isOperational = current.data?.state?.flags?.operational;
            let job = {};
            if (isOperational) {
                const jobResponse = await this.octoprintClient.getJob(this.login);
                job = jobResponse.data;
            }
            this.setApiState(_apistatetype.API_STATE.responding);
            return await this.emitEvent("current", {
                ...current.data,
                progress: job?.progress,
                job: job?.job
            });
        } catch (e) {
            if (e.isAxiosError) {
                const castError = e;
                if (castError?.response?.status == 409) {
                    this.logger.error(`Printer current interval loop error`);
                    await this.emitEvent("current", {
                        state: {
                            flags: {
                                operational: false,
                                error: false
                            },
                            text: "USB disconnected",
                            error: castError?.response.data.error
                        }
                    });
                    return;
                }
                this.logger.error(`Could not update Octoprint current due to a request error`);
                this.setApiState(_apistatetype.API_STATE.noResponse);
                return;
            }
            this.logger.error(`Could not update Octoprint current due to an unknown error`);
            this.setApiState(_apistatetype.API_STATE.noResponse);
        }
    }
    async emitEvent(event, payload) {
        if (!this.eventEmittingAllowed) {
            return;
        }
        await this.eventEmitter2.emitAsync(octoPrintEvent(event), {
            event,
            payload,
            printerId: this.printerId,
            printerType: 0
        });
    }
    async sendAuth() {
        if (!this.sessionDto?.session?.length) {
            throw new Error("Cant send auth, session is unset.");
        }
        this.setSocketState(_socketstatetype.SOCKET_STATE.authenticating);
        await this.sendMessage(JSON.stringify({
            auth: `${this.username}:${this.sessionDto.session}`
        }));
    }
    setSocketState(state) {
        this.socketState = state;
        this.stateUpdated = true;
        this.stateUpdateTimestamp = Date.now();
        if (this._debugMode) {
            this.logger.log(`${this.printerId} Socket state updated to: ` + state);
        }
        this.emitEventSync(WsMessage.WS_STATE_UPDATED, state);
    }
    setApiState(state) {
        if (state === _apistatetype.API_STATE.globalKey) {
            this.logger.warn("Global API Key WS State detected");
        }
        this.apiState = state;
        this.apiStateUpdated = true;
        this.apiStateUpdateTimestamp = Date.now();
        if (this._debugMode) {
            this.logger.log(`${this.printerId} API state updated to: ` + state);
        }
        this.emitEventSync(WsMessage.API_STATE_UPDATED, state);
    }
}

//# sourceMappingURL=octoprint-websocket.adapter.js.map