"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.signRawOperation = exports.makeAccountBridgeReceive = exports.scanAccounts = exports.isInvalidRecipient = exports.signOperation = exports.broadcast = exports.sync = void 0;
// TODO makeMockBridge need to be exploded into families (bridge/mock) with utility code shared.
const account_1 = require("@ledgerhq/coin-framework/mocks/account");
const bignumber_js_1 = require("bignumber.js");
const prando_1 = __importDefault(require("prando"));
const rxjs_1 = require("rxjs");
const live_env_1 = require("@ledgerhq/live-env");
const mock_1 = __importDefault(require("../generated/mock"));
const account_2 = require("../mock/account");
const operation_1 = require("../operation");
const promise_1 = require("../promise");
const MOCK_DATA_SEED = (0, live_env_1.getEnv)("MOCK") || "MOCK";
const broadcasted = {};
const syncTimeouts = {};
const sync = initialAccount => new rxjs_1.Observable(o => {
    const accountId = initialAccount.id;
    const sync = () => {
        const ops = broadcasted[accountId] || [];
        broadcasted[accountId] = [];
        o.next(acc => {
            const balance = ops.reduce((sum, op) => sum.plus((0, operation_1.getOperationAmountNumber)(op)), acc.balance);
            const nextAcc = {
                ...acc,
                blockHeight: acc.blockHeight + 1000,
                lastSyncDate: new Date(),
                operations: ops.concat(acc.operations.slice(0)),
                pendingOperations: [],
                balance,
                spendableBalance: balance,
            };
            const perFamilyOperation = mock_1.default[acc.currency.id];
            const postSyncAccount = perFamilyOperation && perFamilyOperation.postSyncAccount;
            if (postSyncAccount)
                return postSyncAccount(nextAcc);
            return nextAcc;
        });
        o.complete();
    };
    syncTimeouts[accountId] = setTimeout(sync, 500);
    return () => {
        clearTimeout(syncTimeouts[accountId]);
        syncTimeouts[accountId] = null;
    };
});
exports.sync = sync;
const broadcast = ({ signedOperation }) => Promise.resolve(signedOperation.operation);
exports.broadcast = broadcast;
const signOperation = ({ account, transaction }) => new rxjs_1.Observable(o => {
    let cancelled = false;
    async function main() {
        await (0, promise_1.delay)(1000);
        if (cancelled)
            return;
        for (let i = 0; i <= 1; i += 0.1) {
            o.next({
                type: "device-streaming",
                progress: i,
                index: i,
                total: 10,
            });
            await (0, promise_1.delay)(300);
        }
        o.next({
            type: "device-signature-requested",
        });
        await (0, promise_1.delay)(2000);
        if (cancelled)
            return;
        o.next({
            type: "device-signature-granted",
        });
        const rng = new prando_1.default("");
        const op = (0, account_1.genOperation)(account, account, account.operations, rng);
        op.type = "OUT";
        op.value = transaction.amount;
        op.blockHash = null;
        op.blockHeight = null;
        op.senders = [account.freshAddress];
        op.recipients = [transaction.recipient];
        op.blockHeight = account.blockHeight;
        op.date = new Date();
        await (0, promise_1.delay)(1000);
        if (cancelled)
            return;
        broadcasted[account.id] = (broadcasted[account.id] || []).concat(op);
        o.next({
            type: "signed",
            signedOperation: {
                operation: { ...op },
                signature: "",
            },
        });
    }
    main().then(() => o.complete(), e => o.error(e));
    return () => {
        cancelled = true;
    };
});
exports.signOperation = signOperation;
var validateAddress_1 = require("./validateAddress");
Object.defineProperty(exports, "isInvalidRecipient", { enumerable: true, get: function () { return validateAddress_1.isInvalidRecipient; } });
const subtractOneYear = date => new Date(new Date(date).setFullYear(new Date(date).getFullYear() - 1));
const scanAccounts = ({ currency }) => new rxjs_1.Observable(o => {
    let unsubscribed = false;
    async function job() {
        // TODO offer a way to mock a failure
        const nbAccountToGen = 3;
        for (let i = 0; i < nbAccountToGen && !unsubscribed; i++) {
            const isLast = i === 2;
            await (0, promise_1.delay)(500);
            const account = (0, account_2.genAccount)(`${MOCK_DATA_SEED}_${currency.id}_${i}`, {
                operationsSize: isLast ? 0 : 100,
                currency,
                subAccountsCount: isLast ? 0 : undefined,
                bandwidth: !isLast,
            });
            account.index = i;
            account.operations = isLast
                ? []
                : account.operations.map(operation => ({
                    ...operation,
                    date: subtractOneYear(operation.date),
                }));
            account.used = isLast ? false : account.used;
            if (isLast) {
                account.spendableBalance = account.balance = new bignumber_js_1.BigNumber(0);
            }
            const perFamilyOperation = mock_1.default[currency.id];
            const postScanAccount = perFamilyOperation && perFamilyOperation.postScanAccount;
            if (postScanAccount)
                postScanAccount(account, {
                    isEmpty: isLast,
                });
            if (!unsubscribed)
                o.next({
                    type: "discovered",
                    account,
                });
        }
        if (!unsubscribed)
            o.complete();
    }
    job();
    return () => {
        unsubscribed = true;
    };
});
exports.scanAccounts = scanAccounts;
const makeAccountBridgeReceive = () => account => (0, rxjs_1.of)({
    address: account.freshAddress,
    path: account.freshAddressPath,
    publicKey: "mockPublickKey", // We could probably keep the publicKey in `account.freshPublicKey`
});
exports.makeAccountBridgeReceive = makeAccountBridgeReceive;
const signRawOperation = ({ account }) => new rxjs_1.Observable(o => {
    let cancelled = false;
    async function main() {
        await (0, promise_1.delay)(1000);
        if (cancelled)
            return;
        for (let i = 0; i <= 1; i += 0.1) {
            o.next({
                type: "device-streaming",
                progress: i,
                index: i,
                total: 10,
            });
            await (0, promise_1.delay)(300);
        }
        o.next({
            type: "device-signature-requested",
        });
        await (0, promise_1.delay)(2000);
        if (cancelled)
            return;
        o.next({
            type: "device-signature-granted",
        });
        const rng = new prando_1.default("");
        const op = (0, account_1.genOperation)(account, account, account.operations, rng);
        op.type = "OUT";
        op.value = new bignumber_js_1.BigNumber(0);
        op.blockHash = null;
        op.blockHeight = null;
        op.senders = [account.freshAddress];
        op.recipients = [];
        op.blockHeight = account.blockHeight;
        op.date = new Date();
        await (0, promise_1.delay)(1000);
        if (cancelled)
            return;
        broadcasted[account.id] = (broadcasted[account.id] || []).concat(op);
        o.next({
            type: "signed",
            signedOperation: {
                operation: { ...op },
                signature: "",
            },
        });
    }
    main().then(() => o.complete(), e => o.error(e));
    return () => {
        cancelled = true;
    };
});
exports.signRawOperation = signRawOperation;
//# sourceMappingURL=mockHelpers.js.map