"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable camelcase */
const errors_1 = require("@ledgerhq/errors");
const cache_1 = require("@ledgerhq/live-network/cache");
const network_1 = __importDefault(require("@ledgerhq/live-network/network"));
const logs_1 = require("@ledgerhq/logs");
const invariant_1 = __importDefault(require("invariant"));
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const semver_1 = __importDefault(require("semver"));
const url_1 = __importDefault(require("url"));
const package_json_1 = require("../../package.json");
const live_env_1 = require("@ledgerhq/live-env");
const socket_1 = require("../socket");
const socket_mock_1 = require("../socket/socket.mock");
const provider_1 = require("./provider");
const fetchMcusUseCase_1 = require("../device/use-cases/fetchMcusUseCase");
const remapSocketError = (context) => (0, operators_1.catchError)((e) => {
    if (!e || !e.message)
        return (0, rxjs_1.throwError)(() => e);
    if (e.message.startsWith("invalid literal")) {
        // hack to detect the case you're not in good condition (not in dashboard)
        return (0, rxjs_1.throwError)(() => new errors_1.DeviceOnDashboardExpected());
    }
    const status = e instanceof errors_1.TransportStatusError
        ? e.statusCode.toString(16)
        : e.message.slice(e.message.length - 4);
    // TODO use StatusCode instead of this.
    switch (status) {
        case "6a80":
        case "6a81":
        case "6a8e":
        case "6a8f":
            return (0, rxjs_1.throwError)(() => new errors_1.ManagerAppAlreadyInstalledError());
        case "6982":
        case "5303":
            return (0, rxjs_1.throwError)(() => new errors_1.ManagerDeviceLockedError());
        case "6a84":
        case "5103":
            if (context === "firmware" || context === "mcu") {
                return (0, rxjs_1.throwError)(() => new errors_1.ManagerFirmwareNotEnoughSpaceError());
            }
            return (0, rxjs_1.throwError)(() => new errors_1.ManagerNotEnoughSpaceError());
        case "6a85":
        case "5102":
            if (context === "firmware" || context === "mcu") {
                return (0, rxjs_1.throwError)(() => new errors_1.UserRefusedFirmwareUpdate());
            }
            return (0, rxjs_1.throwError)(() => new errors_1.ManagerNotEnoughSpaceError());
        case "6985":
        case "5501":
            if (context === "firmware" || context === "mcu") {
                return (0, rxjs_1.throwError)(() => new errors_1.UserRefusedFirmwareUpdate());
            }
            return (0, rxjs_1.throwError)(() => new errors_1.ManagerNotEnoughSpaceError());
        default:
            return (0, rxjs_1.throwError)(() => e);
    }
});
/** @deprecated use getAppsCatalogForDevice (from ledger-live-common/src/device/use-cases) instead */
const listApps = (0, cache_1.makeLRUCache)(async () => {
    const { data } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/applications`,
            query: {
                livecommonversion: package_json_1.version,
            },
        }),
    });
    if (!data || !Array.isArray(data)) {
        throw new errors_1.NetworkDown("");
    }
    return data;
}, () => (0, live_env_1.getEnv)("MANAGER_API_BASE"));
const listCategories = async () => {
    const r = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/categories`,
            query: {
                livecommonversion: package_json_1.version,
            },
        }),
    });
    return r.data;
};
const compatibleMCUForDeviceInfo = (mcus, deviceInfo, provider) => mcus.filter(m => (deviceInfo.majMin === m.from_bootloader_version ||
    deviceInfo.version === m.from_bootloader_version) &&
    m.providers.includes(provider));
const findBestMCU = (compatibleMCU) => {
    let best = compatibleMCU[0];
    for (let i = 1; i < compatibleMCU.length; i++) {
        if (semver_1.default.gt(semver_1.default.coerce(compatibleMCU[i].name) || "", semver_1.default.coerce(best.name) || "")) {
            best = compatibleMCU[i];
        }
    }
    return best;
};
const getLanguagePackagesForDevice = async (deviceInfo) => {
    const deviceVersion = await getDeviceVersion(deviceInfo.targetId, (0, provider_1.getProviderId)(deviceInfo));
    const seFirmwareVersion = await getCurrentFirmware({
        version: deviceInfo.version,
        deviceId: deviceVersion.id,
        provider: (0, provider_1.getProviderId)(deviceInfo),
    });
    const { data } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/language-package`,
            query: {
                livecommonversion: package_json_1.version,
            },
        }),
    });
    const allPackages = data.reduce((acc, response) => [
        ...acc,
        ...response.language_package_version.map(p => ({
            ...p,
            language: response.language,
        })),
    ], []);
    const packages = allPackages.filter(pack => pack.device_versions.includes(deviceVersion.id) &&
        pack.se_firmware_final_versions.includes(seFirmwareVersion.id));
    return packages;
};
const getCurrentOSU = (0, cache_1.makeLRUCache)(async (input) => {
    const { data } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/get_osu_version`,
            query: {
                livecommonversion: package_json_1.version,
                device_version: input.deviceId,
                version_name: `${input.version}-osu`,
                provider: input.provider,
            },
        }),
    });
    return data;
}, a => `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}_${a.version}_${a.deviceId}_${a.provider}`);
const getCurrentFirmware = (0, cache_1.makeLRUCache)(async (input) => {
    const { data, } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/get_firmware_version`,
            query: {
                livecommonversion: package_json_1.version,
                device_version: input.deviceId,
                version_name: input.version,
                provider: input.provider,
            },
        }),
    }).catch(error => {
        const status = error?.status || error?.response?.status;
        if (status === 404)
            throw new errors_1.FirmwareNotRecognized();
        throw error;
    });
    return data;
}, a => `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}_${a.version}_${a.deviceId}_${a.provider}`);
const getFinalFirmwareById = (0, cache_1.makeLRUCache)(async (id) => {
    const { data, } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/firmware_final_versions/${id}`,
            query: {
                livecommonversion: package_json_1.version,
            },
        }),
    });
    return data;
}, id => `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}}_${String(id)}`);
const getDeviceVersion = (0, cache_1.makeLRUCache)(async (targetId, provider) => {
    const { data, } = await (0, network_1.default)({
        method: "GET",
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}/get_device_version`,
            query: {
                livecommonversion: package_json_1.version,
                provider,
                target_id: targetId,
            },
        }),
    }).catch(error => {
        const status = error?.status || error?.response?.status;
        if (status === 404)
            throw new errors_1.FirmwareNotRecognized("manager api did not recognize targetId=" + targetId, {
                targetId,
            });
        throw error;
    });
    return data;
}, (targetId, provider) => `${(0, live_env_1.getEnv)("MANAGER_API_BASE")}_${targetId}_${provider}`);
const install = (transport, context, params, unresponsiveExpectedDuringBulk) => {
    if ((0, live_env_1.getEnv)("MOCK")) {
        return (0, socket_mock_1.createMockSocket)((0, socket_mock_1.secureChannelMock)(true), (0, socket_mock_1.bulkSocketMock)(3000));
    }
    (0, logs_1.log)("manager", "install " + context, params);
    return (0, socket_1.createDeviceSocket)(transport, {
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("BASE_SOCKET_URL")}/install`,
            query: { ...params, livecommonversion: package_json_1.version },
        }),
        unresponsiveExpectedDuringBulk,
    }).pipe(remapSocketError(context));
};
const genuineCheck = (transport, { targetId, perso, }) => {
    if ((0, live_env_1.getEnv)("MOCK")) {
        return (0, socket_mock_1.createMockSocket)((0, socket_mock_1.secureChannelMock)(false), (0, socket_mock_1.resultMock)("0000"));
    }
    (0, logs_1.log)("manager", "genuineCheck", {
        targetId,
        perso,
    });
    return (0, socket_1.createDeviceSocket)(transport, {
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("BASE_SOCKET_URL")}/genuine`,
            query: {
                targetId,
                perso,
                livecommonversion: package_json_1.version,
            },
        }),
    }).pipe((0, operators_1.map)(e => {
        if (e.type === "result") {
            return {
                type: "result",
                payload: String(e.payload || ""),
            };
        }
        return e;
    }));
};
const listInstalledApps = (transport, { targetId, perso, }) => {
    if ((0, live_env_1.getEnv)("MOCK")) {
        const result = global._listInstalledApps_mock_result;
        (0, invariant_1.default)(result, "using MOCK, global._listInstalledApps_mock_result must be set");
        return (0, socket_mock_1.createMockSocket)((0, socket_mock_1.secureChannelMock)(false), (0, socket_mock_1.resultMock)(result));
    }
    (0, logs_1.log)("manager", "listInstalledApps", {
        targetId,
        perso,
    });
    return (0, socket_1.createDeviceSocket)(transport, {
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("BASE_SOCKET_URL")}/apps/list`,
            query: {
                targetId,
                perso,
                livecommonversion: package_json_1.version,
            },
        }),
    }).pipe(remapSocketError("listInstalledApps"), (0, operators_1.map)(o => {
        if (o.type === "result") {
            return {
                type: "result",
                payload: [...o.payload].map(a => {
                    (0, invariant_1.default)(typeof a === "object" && a, "payload array item are objects");
                    const { hash, name, hash_code_data } = a;
                    (0, invariant_1.default)(typeof hash === "string", "hash is defined");
                    (0, invariant_1.default)(typeof name === "string", "name is defined");
                    return {
                        hash,
                        name,
                        hash_code_data,
                    };
                }),
            };
        }
        return o;
    }));
};
const installMcu = (transport, context, { targetId, version, }) => {
    if ((0, live_env_1.getEnv)("MOCK")) {
        return (0, socket_mock_1.createMockSocket)((0, socket_mock_1.secureChannelMock)(false), (0, socket_mock_1.bulkSocketMock)(5000));
    }
    (0, logs_1.log)("manager", "installMCU " + context, {
        targetId,
        version,
    });
    return (0, socket_1.createDeviceSocket)(transport, {
        url: url_1.default.format({
            pathname: `${(0, live_env_1.getEnv)("BASE_SOCKET_URL")}/mcu`,
            query: {
                targetId,
                version,
                livecommonversion: package_json_1.version,
            },
        }),
        unresponsiveExpectedDuringBulk: true,
    }).pipe(remapSocketError(context));
};
async function retrieveMcuVersion({ mcu_versions, }) {
    const mcus = await (0, fetchMcusUseCase_1.fetchMcusUseCase)();
    const provider = (0, provider_1.getProviderId)(undefined);
    const availableMcus = mcus.filter(({ id, providers, from_bootloader_version }) => providers.includes(provider) &&
        from_bootloader_version !== "none" &&
        mcu_versions.includes(id));
    return findBestMCU(availableMcus);
}
const API = {
    /** @deprecated use getAppsCatalogForDevice (from ledger-live-common/src/device/use-cases) instead */
    listApps,
    listInstalledApps,
    listCategories,
    getLanguagePackagesForDevice,
    getCurrentOSU,
    compatibleMCUForDeviceInfo,
    findBestMCU,
    getCurrentFirmware,
    getFinalFirmwareById,
    getDeviceVersion,
    install,
    genuineCheck,
    installMcu,
    retrieveMcuVersion,
};
exports.default = API;
//# sourceMappingURL=api.js.map