"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAction = exports.signMessageExec = exports.prepareMessageToSign = void 0;
const errors_1 = require("@ledgerhq/errors");
const logs_1 = require("@ledgerhq/logs");
const invariant_1 = __importDefault(require("invariant"));
const react_1 = require("react");
const rxjs_1 = require("rxjs");
const wallet_api_acre_module_1 = require("@ledgerhq/wallet-api-acre-module");
const hw_signMessage_1 = __importDefault(require("../../generated/hw-signMessage"));
const app_1 = require("../actions/app");
const deviceAccess_1 = require("../deviceAccess");
const ACRESetup_1 = require("../../families/bitcoin/ACRESetup");
const account_1 = require("../../account");
const prepareMessageToSign = (account, message) => {
    const utf8Message = Buffer.from(message, "hex").toString();
    if (!hw_signMessage_1.default[account.currency.family]) {
        throw new Error("Crypto does not support signMessage");
    }
    if ("prepareMessageToSign" in hw_signMessage_1.default[account.currency.family]) {
        return hw_signMessage_1.default[account.currency.family].prepareMessageToSign({
            account,
            message: utf8Message,
        });
    }
    // Default implementation
    return { message: utf8Message };
};
exports.prepareMessageToSign = prepareMessageToSign;
const signMessage = (transport, account, opts) => {
    const { currency } = account;
    let signMessage = hw_signMessage_1.default[currency.family].signMessage;
    if ("type" in opts) {
        switch (opts.type) {
            case wallet_api_acre_module_1.AcreMessageType.Withdraw:
                signMessage = ACRESetup_1.messageSigner.signWithdraw;
                break;
            case wallet_api_acre_module_1.AcreMessageType.SignIn:
                signMessage = ACRESetup_1.messageSigner.signIn;
                break;
            default:
                signMessage = ACRESetup_1.messageSigner.signMessage;
                break;
        }
    }
    (0, invariant_1.default)(signMessage, `signMessage is not implemented for ${currency.id}`);
    return signMessage(transport, account, opts)
        .then(result => {
        const path = "path" in opts && opts.path ? opts.path : account.freshAddressPath;
        (0, logs_1.log)("hw", `signMessage ${currency.id} on ${path} with message [${opts.message}]`, result);
        return result;
    })
        .catch(e => {
        const path = "path" in opts && opts.path ? opts.path : account.freshAddressPath;
        (0, logs_1.log)("hw", `signMessage ${currency.id} on ${path} FAILED ${String(e)}`);
        if (e && e.name === "TransportStatusError") {
            if (e.statusCode === 0x6985 || e.statusCode === 0x5501) {
                throw new errors_1.UserRefusedAddress();
            }
        }
        throw e;
    });
};
const signMessageExec = ({ request, deviceId }) => {
    if (!request.account) {
        throw new Error("account is required");
    }
    const { type } = (0, account_1.decodeAccountId)(request.account.id);
    if (type === "mock") {
        return (0, rxjs_1.from)(Promise.resolve({
            signature: "mockedSignature",
        }));
    }
    const result = (0, deviceAccess_1.withDevice)(deviceId)(transport => (0, rxjs_1.from)(signMessage(transport, request.account, request.message)));
    return result;
};
exports.signMessageExec = signMessageExec;
const initialState = {
    signMessageRequested: null,
    signMessageError: null,
    signMessageResult: null,
};
const createAction = (connectAppExec, signMessage = exports.signMessageExec) => {
    const useHook = (reduxDevice, request) => {
        const appState = (0, app_1.createAction)(connectAppExec).useHook(reduxDevice, {
            appName: request.appName,
            dependencies: request.dependencies,
            account: request.isACRE ? undefined : request.account, // Bypass derivation check with ACRE as we can use other addresses than the freshest
        });
        const { device, opened, inWrongDeviceForAccount, error } = appState;
        const [state, setState] = (0, react_1.useState)({
            ...initialState,
            signMessageRequested: request.message,
        });
        const signedFired = (0, react_1.useRef)();
        const sign = (0, react_1.useCallback)(async () => {
            let result;
            if (!device) {
                setState({
                    ...initialState,
                    signMessageError: new Error("no Device"),
                });
                return;
            }
            try {
                result = await (0, rxjs_1.firstValueFrom)(signMessage({
                    request,
                    deviceId: device.deviceId,
                }));
            }
            catch (e) {
                if (e.name === "UserRefusedAddress") {
                    e.name = "UserRefusedOnDevice";
                    e.message = "UserRefusedOnDevice";
                }
                return setState({ ...initialState, signMessageError: e });
            }
            setState({ ...initialState, signMessageResult: result?.signature });
        }, [device, request]);
        (0, react_1.useEffect)(() => {
            if (!device || !opened || inWrongDeviceForAccount || error) {
                return;
            }
            if (state.signMessageRequested && !signedFired.current) {
                signedFired.current = true;
                sign();
            }
        }, [device, opened, inWrongDeviceForAccount, error, sign, state.signMessageRequested]);
        return { ...appState, ...state };
    };
    return {
        useHook,
        mapResult: (state) => ({
            signature: state.signMessageResult,
            error: state.signMessageError,
        }),
    };
};
exports.createAction = createAction;
exports.default = signMessage;
//# sourceMappingURL=index.js.map