/* eslint-disable camelcase */
import { DeviceOnDashboardExpected, FirmwareNotRecognized, ManagerAppAlreadyInstalledError, ManagerDeviceLockedError, ManagerFirmwareNotEnoughSpaceError, ManagerNotEnoughSpaceError, NetworkDown, TransportStatusError, UserRefusedFirmwareUpdate, } from "@ledgerhq/errors";
import { makeLRUCache } from "@ledgerhq/live-network/cache";
import network from "@ledgerhq/live-network/network";
import { log } from "@ledgerhq/logs";
import invariant from "invariant";
import { throwError } from "rxjs";
import { catchError, map } from "rxjs/operators";
import semver from "semver";
import URL from "url";
import { version as livecommonversion } from "../../package.json";
import { getEnv } from "@ledgerhq/live-env";
import { createDeviceSocket } from "../socket";
import { bulkSocketMock, createMockSocket, resultMock, secureChannelMock, } from "../socket/socket.mock";
import { getProviderId } from "./provider";
import { fetchMcusUseCase } from "../device/use-cases/fetchMcusUseCase";
const remapSocketError = (context) => catchError((e) => {
    if (!e || !e.message)
        return throwError(() => e);
    if (e.message.startsWith("invalid literal")) {
        // hack to detect the case you're not in good condition (not in dashboard)
        return throwError(() => new DeviceOnDashboardExpected());
    }
    const status = e instanceof 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 throwError(() => new ManagerAppAlreadyInstalledError());
        case "6982":
        case "5303":
            return throwError(() => new ManagerDeviceLockedError());
        case "6a84":
        case "5103":
            if (context === "firmware" || context === "mcu") {
                return throwError(() => new ManagerFirmwareNotEnoughSpaceError());
            }
            return throwError(() => new ManagerNotEnoughSpaceError());
        case "6a85":
        case "5102":
            if (context === "firmware" || context === "mcu") {
                return throwError(() => new UserRefusedFirmwareUpdate());
            }
            return throwError(() => new ManagerNotEnoughSpaceError());
        case "6985":
        case "5501":
            if (context === "firmware" || context === "mcu") {
                return throwError(() => new UserRefusedFirmwareUpdate());
            }
            return throwError(() => new ManagerNotEnoughSpaceError());
        default:
            return throwError(() => e);
    }
});
/** @deprecated use getAppsCatalogForDevice (from ledger-live-common/src/device/use-cases) instead */
const listApps = makeLRUCache(async () => {
    const { data } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/applications`,
            query: {
                livecommonversion,
            },
        }),
    });
    if (!data || !Array.isArray(data)) {
        throw new NetworkDown("");
    }
    return data;
}, () => getEnv("MANAGER_API_BASE"));
const listCategories = async () => {
    const r = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/categories`,
            query: {
                livecommonversion,
            },
        }),
    });
    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.gt(semver.coerce(compatibleMCU[i].name) || "", semver.coerce(best.name) || "")) {
            best = compatibleMCU[i];
        }
    }
    return best;
};
const getLanguagePackagesForDevice = async (deviceInfo) => {
    const deviceVersion = await getDeviceVersion(deviceInfo.targetId, getProviderId(deviceInfo));
    const seFirmwareVersion = await getCurrentFirmware({
        version: deviceInfo.version,
        deviceId: deviceVersion.id,
        provider: getProviderId(deviceInfo),
    });
    const { data } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/language-package`,
            query: {
                livecommonversion,
            },
        }),
    });
    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 = makeLRUCache(async (input) => {
    const { data } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/get_osu_version`,
            query: {
                livecommonversion,
                device_version: input.deviceId,
                version_name: `${input.version}-osu`,
                provider: input.provider,
            },
        }),
    });
    return data;
}, a => `${getEnv("MANAGER_API_BASE")}_${a.version}_${a.deviceId}_${a.provider}`);
const getCurrentFirmware = makeLRUCache(async (input) => {
    const { data, } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/get_firmware_version`,
            query: {
                livecommonversion,
                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 FirmwareNotRecognized();
        throw error;
    });
    return data;
}, a => `${getEnv("MANAGER_API_BASE")}_${a.version}_${a.deviceId}_${a.provider}`);
const getFinalFirmwareById = makeLRUCache(async (id) => {
    const { data, } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/firmware_final_versions/${id}`,
            query: {
                livecommonversion,
            },
        }),
    });
    return data;
}, id => `${getEnv("MANAGER_API_BASE")}}_${String(id)}`);
const getDeviceVersion = makeLRUCache(async (targetId, provider) => {
    const { data, } = await network({
        method: "GET",
        url: URL.format({
            pathname: `${getEnv("MANAGER_API_BASE")}/get_device_version`,
            query: {
                livecommonversion,
                provider,
                target_id: targetId,
            },
        }),
    }).catch(error => {
        const status = error?.status || error?.response?.status;
        if (status === 404)
            throw new FirmwareNotRecognized("manager api did not recognize targetId=" + targetId, {
                targetId,
            });
        throw error;
    });
    return data;
}, (targetId, provider) => `${getEnv("MANAGER_API_BASE")}_${targetId}_${provider}`);
const install = (transport, context, params, unresponsiveExpectedDuringBulk) => {
    if (getEnv("MOCK")) {
        return createMockSocket(secureChannelMock(true), bulkSocketMock(3000));
    }
    log("manager", "install " + context, params);
    return createDeviceSocket(transport, {
        url: URL.format({
            pathname: `${getEnv("BASE_SOCKET_URL")}/install`,
            query: { ...params, livecommonversion },
        }),
        unresponsiveExpectedDuringBulk,
    }).pipe(remapSocketError(context));
};
const genuineCheck = (transport, { targetId, perso, }) => {
    if (getEnv("MOCK")) {
        return createMockSocket(secureChannelMock(false), resultMock("0000"));
    }
    log("manager", "genuineCheck", {
        targetId,
        perso,
    });
    return createDeviceSocket(transport, {
        url: URL.format({
            pathname: `${getEnv("BASE_SOCKET_URL")}/genuine`,
            query: {
                targetId,
                perso,
                livecommonversion,
            },
        }),
    }).pipe(map(e => {
        if (e.type === "result") {
            return {
                type: "result",
                payload: String(e.payload || ""),
            };
        }
        return e;
    }));
};
const listInstalledApps = (transport, { targetId, perso, }) => {
    if (getEnv("MOCK")) {
        const result = global._listInstalledApps_mock_result;
        invariant(result, "using MOCK, global._listInstalledApps_mock_result must be set");
        return createMockSocket(secureChannelMock(false), resultMock(result));
    }
    log("manager", "listInstalledApps", {
        targetId,
        perso,
    });
    return createDeviceSocket(transport, {
        url: URL.format({
            pathname: `${getEnv("BASE_SOCKET_URL")}/apps/list`,
            query: {
                targetId,
                perso,
                livecommonversion,
            },
        }),
    }).pipe(remapSocketError("listInstalledApps"), map(o => {
        if (o.type === "result") {
            return {
                type: "result",
                payload: [...o.payload].map(a => {
                    invariant(typeof a === "object" && a, "payload array item are objects");
                    const { hash, name, hash_code_data } = a;
                    invariant(typeof hash === "string", "hash is defined");
                    invariant(typeof name === "string", "name is defined");
                    return {
                        hash,
                        name,
                        hash_code_data,
                    };
                }),
            };
        }
        return o;
    }));
};
const installMcu = (transport, context, { targetId, version, }) => {
    if (getEnv("MOCK")) {
        return createMockSocket(secureChannelMock(false), bulkSocketMock(5000));
    }
    log("manager", "installMCU " + context, {
        targetId,
        version,
    });
    return createDeviceSocket(transport, {
        url: URL.format({
            pathname: `${getEnv("BASE_SOCKET_URL")}/mcu`,
            query: {
                targetId,
                version,
                livecommonversion,
            },
        }),
        unresponsiveExpectedDuringBulk: true,
    }).pipe(remapSocketError(context));
};
async function retrieveMcuVersion({ mcu_versions, }) {
    const mcus = await fetchMcusUseCase();
    const provider = 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,
};
export default API;
//# sourceMappingURL=api.js.map