"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isAllowedOnboardingStatePollingError = exports.getOnboardingStatePolling = void 0;
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const getVersionUseCase_1 = require("../device/use-cases/getVersionUseCase");
const deviceAccess_1 = require("./deviceAccess");
const errors_1 = require("@ledgerhq/errors");
const extractOnboardingState_1 = require("./extractOnboardingState");
const device_management_kit_1 = require("@ledgerhq/device-management-kit");
const quitApp_1 = require("../deviceSDK/commands/quitApp");
/**
 * Polls the device onboarding state at a given frequency
 *
 * @param deviceId A device id
 * @param pollingPeriodMs The period in ms after which the device onboarding state is fetched again
 * @param transportAbortTimeoutMs Depending on the transport implementation, an "abort timeout" will be set (and throw an error) when:
 *  - opening a transport instance
 *  - on called commands (where `getVersion`)
 *  Default to (pollingPeriodMs - 100) ms
 * @param safeGuardTimeoutMs For Transport implementations not implementing an "abort timeout", a timeout will be triggered (and throw an error) at this function call level
 * @returns An Observable that polls the device onboarding state and pushes an object containing:
 * - onboardingState: the device state during the onboarding
 * - allowedError: any error that is allowed and does not stop the polling
 * - lockedDevice: a boolean set to true if the device is currently locked, false otherwise
 */
const getOnboardingStatePolling = ({ deviceId, deviceName, pollingPeriodMs, transportAbortTimeoutMs = pollingPeriodMs - 100, safeGuardTimeoutMs = pollingPeriodMs * 10, // Nb Empirical value
allowedErrorChecks = [], }) => {
    let hasQuitAppAlreadyRun = false;
    const getOnboardingStateOnce = () => {
        return (0, deviceAccess_1.withDevice)(deviceId, {
            openTimeoutMs: transportAbortTimeoutMs,
            matchDeviceByName: deviceName ?? undefined,
        })(t => {
            const getVersionObs = (0, rxjs_1.from)((0, getVersionUseCase_1.getVersion)(t, { abortTimeoutMs: transportAbortTimeoutMs }));
            // only run quitApp the first time
            if (hasQuitAppAlreadyRun) {
                return getVersionObs;
            }
            hasQuitAppAlreadyRun = true;
            return (0, quitApp_1.quitApp)(t).pipe((0, operators_1.switchMap)(() => getVersionObs));
        }).pipe((0, operators_1.timeout)(safeGuardTimeoutMs), // Throws a TimeoutError
        (0, operators_1.first)(), (0, operators_1.catchError)((error) => {
            if ((0, exports.isAllowedOnboardingStatePollingError)(error) ||
                allowedErrorChecks?.some(fn => fn(error))) {
                // Pushes the error to the next step to be processed (no retry from the beginning)
                return (0, rxjs_1.of)(error);
            }
            return (0, rxjs_1.throwError)(() => error);
        }), (0, operators_1.map)((event) => {
            if ("flags" in event) {
                const firmwareInfo = event;
                let onboardingState = null;
                if (firmwareInfo.isBootloader) {
                    // Throws so it will be considered a fatal error
                    throw new errors_1.UnexpectedBootloader("Device in bootloader during the polling");
                }
                try {
                    onboardingState = (0, extractOnboardingState_1.extractOnboardingState)(firmwareInfo.flags, firmwareInfo.charonState);
                }
                catch (error) {
                    if (error instanceof errors_1.DeviceExtractOnboardingStateError) {
                        return {
                            onboardingState: null,
                            allowedError: error,
                            lockedDevice: false,
                        };
                    }
                    else {
                        let errorMessage = "";
                        if (error instanceof Error) {
                            errorMessage = `${error.name}: ${error.message}`;
                        }
                        else {
                            errorMessage = `${error}`;
                        }
                        return {
                            onboardingState: null,
                            allowedError: new errors_1.DeviceOnboardingStatePollingError(`SyncOnboarding: Unknown error while extracting the onboarding state ${errorMessage}`),
                            lockedDevice: false,
                        };
                    }
                }
                return {
                    onboardingState,
                    allowedError: null,
                    lockedDevice: false,
                };
            }
            else {
                // If an error is caught previously, and this error is "allowed",
                // the value from the observable is not a FirmwareInfo but an Error
                const allowedError = event;
                return {
                    onboardingState: null,
                    allowedError: allowedError,
                    lockedDevice: allowedError instanceof errors_1.LockedDeviceError,
                };
            }
        }));
    };
    return getOnboardingStateOnce().pipe((0, operators_1.repeat)({
        delay: _count => (0, rxjs_1.timer)(pollingPeriodMs),
    }));
};
exports.getOnboardingStatePolling = getOnboardingStatePolling;
const isAllowedOnboardingStatePollingError = (error) => {
    if (error &&
        // Timeout error is thrown by rxjs's timeout
        (error instanceof rxjs_1.TimeoutError ||
            error instanceof errors_1.TransportExchangeTimeoutError ||
            error instanceof errors_1.DisconnectedDevice ||
            error instanceof errors_1.DisconnectedDeviceDuringOperation ||
            error instanceof device_management_kit_1.DeviceDisconnectedWhileSendingError ||
            error instanceof errors_1.CantOpenDevice ||
            error instanceof errors_1.TransportRaceCondition ||
            error instanceof errors_1.TransportStatusError ||
            // A locked device is handled as an allowed error
            error instanceof errors_1.LockedDeviceError)) {
        return true;
    }
    return false;
};
exports.isAllowedOnboardingStatePollingError = isAllowedOnboardingStatePollingError;
//# sourceMappingURL=getOnboardingStatePolling.js.map