"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.repairChoices = void 0;
const logs_1 = require("@ledgerhq/logs");
const errors_1 = require("@ledgerhq/errors");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const semver_1 = __importDefault(require("semver"));
const api_1 = __importDefault(require("../manager/api"));
const deviceAccess_1 = require("./deviceAccess");
const provider_1 = require("../manager/provider");
const getDeviceInfo_1 = __importDefault(require("./getDeviceInfo"));
const deviceWordings_1 = require("../deviceWordings");
const getDeviceRunningMode_1 = require("./getDeviceRunningMode");
const fetchMcusUseCase_1 = require("../device/use-cases/fetchMcusUseCase");
const wait2s = (0, rxjs_1.of)({
    type: "wait",
}).pipe((0, operators_1.delay)(2000));
exports.repairChoices = [
    {
        id: "mcuOutdated",
        label: deviceWordings_1.mcuOutdated,
        forceMCU: "0.7",
    },
    {
        id: "mcuNotGenuine",
        label: deviceWordings_1.mcuNotGenuine,
        forceMCU: "0.7",
    },
    {
        id: "followDeviceRepair",
        label: deviceWordings_1.followDeviceRepair,
        forceMCU: "0.9",
    },
    {
        id: "followDeviceUpdate",
        label: deviceWordings_1.followDeviceUpdate,
        forceMCU: "0.9",
    },
];
const repair = (deviceId, forceMCU_) => {
    (0, logs_1.log)("hw", "firmwareUpdate-repair");
    const mcusPromise = (0, fetchMcusUseCase_1.fetchMcusUseCase)();
    const withDeviceInfo = (0, deviceAccess_1.withDevicePolling)(deviceId)(transport => (0, rxjs_1.from)((0, getDeviceInfo_1.default)(transport)), () => true);
    const waitForBootloader = withDeviceInfo.pipe((0, operators_1.concatMap)(deviceInfo => (deviceInfo.isBootloader ? rxjs_1.EMPTY : (0, rxjs_1.concat)(wait2s, waitForBootloader))));
    const loop = (forceMCU) => (0, rxjs_1.concat)(withDeviceInfo.pipe((0, operators_1.concatMap)(deviceInfo => {
        const installMcu = (version) => (0, deviceAccess_1.withDevice)(deviceId)(transport => api_1.default.installMcu(transport, "mcu", {
            targetId: deviceInfo.targetId,
            version,
        }));
        if (!deviceInfo.isBootloader) {
            // finish earlier
            return rxjs_1.EMPTY;
        }
        // This is a special case where user is in firmware 1.3.1
        // and the device shows MCU Not Genuine.
        // User needs to press both keys three times to go back to dashboard
        // and continue the update process
        if (forceMCU &&
            forceMCU === "0.7" &&
            (deviceInfo.majMin === "0.6" || deviceInfo.majMin === "0.7")) {
            // finish earlier
            return (0, rxjs_1.throwError)(() => new errors_1.MCUNotGenuineToDashboard());
        }
        if (forceMCU) {
            return (0, rxjs_1.concat)(installMcu(forceMCU), wait2s, loop());
        }
        switch (deviceInfo.majMin) {
            case "0.0":
                return (0, rxjs_1.concat)(installMcu("0.6"), wait2s, loop());
            case "0.6":
                return installMcu("1.5");
            case "0.7":
                return installMcu("1.6");
            case "0.9":
                return installMcu("1.7");
            default:
                return (0, rxjs_1.from)(mcusPromise).pipe((0, operators_1.concatMap)(mcus => {
                    let next;
                    const { seVersion, seTargetId, mcuBlVersion } = deviceInfo;
                    // This is a special case where a user with LNX version >= 2.0.0
                    // comes back with a broken updated device. We need to be able
                    // to patch MCU or Bootloader if needed
                    if (seVersion && seTargetId) {
                        (0, logs_1.log)("hw", "firmwareUpdate-repair seVersion and seTargetId found", {
                            seVersion,
                            seTargetId,
                        });
                        const provider = (0, provider_1.getProviderId)(deviceInfo);
                        /**
                         * filter the MCUs that are available on the provider and
                         * have a "from_bootloader_version" different from "none"
                         * */
                        const availableMcus = mcus.filter(mcu => mcu.providers.includes(provider) && mcu.from_bootloader_version !== "none");
                        (0, logs_1.log)("hw", `firmwareUpdate-repair available mcus on provider ${provider}`, {
                            availableMcus,
                        });
                        return (0, rxjs_1.from)(api_1.default.getDeviceVersion(seTargetId, (0, provider_1.getProviderId)(deviceInfo))).pipe((0, operators_1.mergeMap)((deviceVersion) => (0, rxjs_1.from)(api_1.default.getCurrentFirmware({
                            deviceId: deviceVersion.id,
                            version: seVersion,
                            provider: (0, provider_1.getProviderId)(deviceInfo),
                        }))), (0, operators_1.mergeMap)((finalFirmware) => {
                            (0, logs_1.log)("hw", "firmwareUpdate-repair got final firmware", {
                                finalFirmware,
                            });
                            const mcu = api_1.default.findBestMCU(availableMcus.filter(({ id }) => finalFirmware.mcu_versions.includes(id)));
                            (0, logs_1.log)("hw", "firmwareUpdate-repair got mcu", { mcu });
                            if (!mcu)
                                return rxjs_1.EMPTY;
                            const expectedBootloaderVersion = semver_1.default.coerce(mcu.from_bootloader_version)?.version;
                            const currentBootloaderVersion = semver_1.default.coerce(mcuBlVersion)?.version;
                            (0, logs_1.log)("hw", "firmwareUpdate-repair bootloader versions", {
                                currentBootloaderVersion,
                                expectedBootloaderVersion,
                            });
                            if (expectedBootloaderVersion === currentBootloaderVersion) {
                                next = mcu;
                                (0, logs_1.log)("hw", "firmwareUpdate-repair bootloader versions are the same", {
                                    next,
                                });
                            }
                            else {
                                next = {
                                    name: mcu.from_bootloader_version,
                                };
                                (0, logs_1.log)("hw", "firmwareUpdate-repair bootloader versions are different", {
                                    next,
                                });
                            }
                            return installMcu(next.name);
                        }));
                    }
                    else {
                        next = api_1.default.findBestMCU(api_1.default.compatibleMCUForDeviceInfo(mcus, deviceInfo, (0, provider_1.getProviderId)(deviceInfo)));
                        if (next)
                            return installMcu(next.name);
                    }
                    return rxjs_1.EMPTY;
                }));
        }
    })), (0, rxjs_1.from)((0, getDeviceRunningMode_1.getDeviceRunningMode)({
        deviceId,
        unresponsiveTimeoutMs: 4000,
        cantOpenDeviceRetryLimit: 2,
    })).pipe((0, operators_1.mergeMap)(result => {
        if (result.type === "bootloaderMode") {
            return loop(forceMCU);
        }
        else {
            return rxjs_1.EMPTY;
        }
    })));
    // TODO ideally we should race waitForBootloader with an event "display-bootloader-reboot", it should be a delayed event that is not emitted if waitForBootloader is fast enough..
    return (0, rxjs_1.concat)(waitForBootloader, loop(forceMCU_)).pipe((0, operators_1.filter)((e) => e.type === "bulk-progress"), (0, operators_1.map)(e => ({
        progress: e.progress,
    })), (0, operators_1.throttleTime)(100));
};
exports.default = repair;
//# sourceMappingURL=firmwareUpdate-repair.js.map