import { DisconnectedDevice, UnresponsiveDeviceError } from "@ledgerhq/errors";
import { log } from "@ledgerhq/logs";
import { getVersion } from "../commands/getVersion";
import isDevFirmware from "../../hw/isDevFirmware";
import { PROVIDERS } from "../../manager/provider";
import { Observable } from "rxjs";
import { map, switchMap } from "rxjs/operators";
import { retryOnErrorsCommandWrapper, sharedLogicTaskWrapper } from "./core";
import { quitApp } from "../commands/quitApp";
import { withTransport } from "../transports/core";
import { SendApduEmptyResponseError } from "@ledgerhq/device-management-kit";
const ManagerAllowedFlag = 0x08;
const PinValidatedFlag = 0x80;
// Exported for tests
export function internalGetDeviceInfoTask({ deviceId, deviceName, }) {
    return new Observable(subscriber => {
        return (withTransport(deviceId, deviceName ? { matchDeviceByName: deviceName } : undefined)(({ transportRef }) => quitApp(transportRef.current).pipe(switchMap(() => {
            return retryOnErrorsCommandWrapper({
                command: getVersion,
                allowedErrors: [{ maxRetries: 3, errorClass: DisconnectedDevice }],
                allowedDmkErrors: [new SendApduEmptyResponseError()],
            })(transportRef, {});
        }), map(value => {
            if (value.type === "unresponsive") {
                return {
                    type: "error",
                    error: new UnresponsiveDeviceError(),
                    retrying: true,
                };
            }
            const { firmwareInfo } = value;
            const deviceInfo = parseDeviceInfo(firmwareInfo);
            return { type: "data", deviceInfo };
        })))
            // Any error will be handled by the sharedLogicTaskWrapper, which will map it a relevant event
            .subscribe(subscriber));
    });
}
export const parseDeviceInfo = (firmwareInfo) => {
    const { isBootloader, rawVersion, targetId, seVersion, seTargetId, mcuBlVersion, mcuVersion, mcuTargetId, flags, bootloaderVersion, hardwareVersion, languageId, charonState, } = firmwareInfo;
    const isOSU = rawVersion.includes("-osu");
    const version = rawVersion.replace("-osu", "");
    const m = rawVersion.match(/([0-9]+.[0-9]+(.[0-9]+){0,1})?(-(.*))?/);
    const [, majMin, , , postDash] = m || [];
    const providerName = PROVIDERS[postDash] ? postDash : null;
    const flag = flags.length > 0 ? flags[0] : 0;
    const managerAllowed = !!(flag & ManagerAllowedFlag);
    const pinValidated = !!(flag & PinValidatedFlag);
    let isRecoveryMode = false;
    let onboarded = true;
    if (flags.length === 4) {
        // Nb Since LNS+ unseeded devices are visible + extra flags
        isRecoveryMode = !!(flags[0] & 0x01);
        onboarded = !!(flags[0] & 0x04);
    }
    log("hw", "deviceInfo: se@" +
        version +
        " mcu@" +
        mcuVersion +
        (isOSU ? " (osu)" : isBootloader ? " (bootloader)" : ""));
    const hasDevFirmware = isDevFirmware(seVersion);
    const deviceInfo = {
        version,
        mcuVersion,
        seVersion,
        mcuBlVersion,
        majMin,
        providerName: providerName || null,
        targetId,
        hasDevFirmware,
        seTargetId,
        mcuTargetId,
        isOSU,
        isBootloader,
        isRecoveryMode,
        managerAllowed,
        pinValidated,
        onboarded,
        bootloaderVersion,
        hardwareVersion,
        languageId,
        seFlags: flags,
        charonState: charonState,
    };
    return deviceInfo;
};
/**
 * Task to get the `DeviceInfo` of a device
 *
 * @param `deviceId` A device id, or an empty string if device is usb plugged
 * @returns An observable that emits `GetDeviceInfoTaskEvent` events
 */
export const getDeviceInfoTask = sharedLogicTaskWrapper(internalGetDeviceInfoTask);
//# sourceMappingURL=getDeviceInfo.js.map