"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TxQueueModal = void 0;
const react_1 = __importStar(require("react"));
const polymesh_ui_1 = require("@polymathnetwork/polymesh-ui");
const types_1 = require("@polymathnetwork/polymesh-sdk/types");
const react_i18next_1 = require("react-i18next");
const definition_1 = require("./definition");
// A store of data that was harvested from the previous tx events.
let previousTx = {};
exports.TxQueueModal = ({ sdk, store, signAndSend, transactionActions, explorerUrl }) => {
    const { t } = react_i18next_1.useTranslation();
    const [transactions, setTransactions] = react_1.useState([]);
    const [currentTransaction, setCurrentTransaction] = react_1.useState(0);
    const [queue, setQueue] = react_1.useState([]);
    const [isOpen, setIsOpen] = react_1.useState(false);
    const [canDismiss, setCanDismiss] = react_1.useState(false);
    const [isFinalized, setIsFinalized] = react_1.useState(false);
    const [hasErrors, setHasErrors] = react_1.useState(false);
    const [failure, setFailure] = react_1.useState(false);
    const [txHistory, setTxHistory] = react_1.useState([]);
    const { dispatch, getState } = store;
    const { transaction: { signingPackage }, } = getState();
    const { id: signingPackageId, queue: txQueue, apiQueue, confirmationStatus, onCompleted, failUrl, } = signingPackage || {};
    const isApi = !!apiQueue;
    const onTransactionStatusChange = (tx, txQueueObj) => {
        setTransactions([...txQueueObj.transactions]);
    };
    const onProcessedByMiddleware = react_1.useCallback((error) => {
        dispatch(transactionActions.queueProcessedByMiddleware({
            queue: txQueue,
            error,
        }));
    }, [txQueue, dispatch]);
    const onQueueStatusChange = (newTxQueue) => {
        if (newTxQueue.status === types_1.TransactionQueueStatus.Succeeded ||
            newTxQueue.status === types_1.TransactionQueueStatus.Failed) {
            setIsFinalized(true);
            setFailure(newTxQueue.status === types_1.TransactionQueueStatus.Failed);
        }
        else {
            setIsFinalized(false);
        }
    };
    const runQueueSdk = react_1.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
        if (!txQueue)
            return;
        try {
            setTransactions([...txQueue.transactions]);
            txQueue.onTransactionStatusChange(onTransactionStatusChange);
            txQueue.onStatusChange(onQueueStatusChange);
            txQueue.onProcessedByMiddleware(onProcessedByMiddleware);
            yield txQueue.run();
        }
        catch (e) { } // eslint-disable-line no-empty
    }), [txQueue, onProcessedByMiddleware]);
    const runQueueApi = react_1.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
        if (!apiQueue)
            return;
        setTransactions(apiQueue);
        setQueue(apiQueue);
        setCurrentTransaction(0);
        setIsFinalized(false);
        setHasErrors(false);
        setFailure(false);
    }), [apiQueue]);
    react_1.useEffect(() => {
        const runQueue = () => __awaiter(void 0, void 0, void 0, function* () {
            if (confirmationStatus === definition_1.ConfirmationStatus.Done) {
                if (isApi) {
                    yield runQueueApi();
                }
                else {
                    yield runQueueSdk();
                }
            }
        });
        runQueue();
    }, [txQueue, apiQueue, confirmationStatus, isApi, runQueueApi, runQueueSdk]);
    // Process next transaction if using API
    react_1.useEffect(() => {
        if (isApi && queue.length > 0 && sdk) {
            const processTransaction = (tx) => __awaiter(void 0, void 0, void 0, function* () {
                try {
                    const tempQueue = [...queue];
                    // Set the transaction status to minting
                    tempQueue.splice(currentTransaction, 1, Object.assign(Object.assign({}, queue[currentTransaction]), { status: types_1.TransactionStatus.Running }));
                    setQueue([...tempQueue]);
                    // Submit the transaction
                    if (tx.type === definition_1.ApiQueueItemType.Batch) {
                        const batchParams = tx.params.map((param) => {
                            if (param.method !== undefined &&
                                typeof param.method === 'function' &&
                                param.params !== undefined) {
                                const methodParams = param.params.map((p) => {
                                    if (typeof p === 'function') {
                                        return p(previousTx);
                                    }
                                    return p;
                                });
                                return param.method(...methodParams);
                            }
                            return null;
                        });
                        tx.params = [batchParams];
                    }
                    else {
                        tx.params = tx.params.map((param) => {
                            // this param is actually a function that reads the value of a param
                            // from previous tx store.
                            if (typeof param === 'function') {
                                return param(previousTx);
                            }
                            return param;
                        });
                    }
                    const [status, isError, data] = yield signAndSend(sdk, tx.method, tx.params, tx.dataResolver);
                    previousTx = Object.assign(Object.assign({}, previousTx), data);
                    // Update the queue
                    tempQueue.splice(currentTransaction, 1, Object.assign(Object.assign({}, queue[currentTransaction]), { status: isError
                            ? types_1.TransactionStatus.Failed
                            : types_1.TransactionStatus.Succeeded, txHash: status.hash.toString() }));
                    // Determine what to do next
                    if (isError) {
                        // The transaction failed
                        if (tx.blocking) {
                            // The failed transaction is a show stopper
                            setFailure(true);
                        }
                        else {
                            // non show-stopper
                            setHasErrors(true);
                        }
                    }
                    if (transactions) {
                        setTransactions(transactions.map((transaction, index) => {
                            if (index === currentTransaction) {
                                return Object.assign(Object.assign({}, transaction), { status: isError
                                        ? types_1.TransactionStatus.Failed
                                        : types_1.TransactionStatus.Succeeded });
                            }
                            return transaction;
                        }));
                    }
                    setCurrentTransaction(currentTransaction + 1);
                    setQueue([...tempQueue]);
                }
                catch (e) {
                    if (e.message === 'Cancelled') {
                        setFailure(true);
                        const tempQueue = [...queue];
                        tempQueue.splice(currentTransaction, 1, Object.assign(Object.assign({}, queue[currentTransaction]), { status: types_1.TransactionStatus.Rejected }));
                        setQueue([...tempQueue]);
                    }
                    else if (e.message === '1010: Invalid Transaction: Payment') {
                        setFailure(true);
                        const tempQueue = [...queue];
                        tempQueue.splice(currentTransaction, 1, Object.assign(Object.assign({}, queue[currentTransaction]), { status: types_1.TransactionStatus.Failed }));
                        setQueue([...tempQueue]);
                    }
                    else {
                        setFailure(true);
                        const tempQueue = [...queue];
                        tempQueue.splice(currentTransaction, 1, Object.assign(Object.assign({}, queue[currentTransaction]), { status: types_1.TransactionStatus.Failed }));
                        setQueue([...tempQueue]);
                    }
                }
            });
            // Look if there is a transaction in progress
            const inProgress = queue.find((item) => {
                return item.status === types_1.TransactionStatus.Running;
            });
            if (inProgress) {
                // There is already a transaction in progress
                return;
            }
            if (queue.length === 0) {
                // Empty queue
            }
            else if (currentTransaction >= queue.length || failure) {
                // All done
                setIsFinalized(true);
            }
            else {
                // Get the next item from the queue
                const tx = queue[currentTransaction];
                // Process the transaction
                processTransaction(tx);
            }
        }
    }, [isApi, queue, confirmationStatus, currentTransaction, sdk]); // eslint-disable-line
    const getQueueStatus = () => transactions.filter((tq) => tq.status === types_1.TransactionStatus.Running).length
        ? types_1.TransactionStatus.Running
        : (transactions || queue).filter((tq) => tq.status === types_1.TransactionStatus.Failed ||
            tq.status === types_1.TransactionStatus.Rejected ||
            tq.status === types_1.TransactionStatus.Aborted).length
            ? types_1.TransactionStatus.Failed
            : (transactions || queue).filter((tq) => tq.status === types_1.TransactionStatus.Succeeded).length
                ? types_1.TransactionStatus.Succeeded
                : types_1.TransactionStatus.Idle;
    const renderStatusIcon = (status) => {
        const fail = (react_1.default.createElement(polymesh_ui_1.IconOutlined, { Asset: polymesh_ui_1.icons.SvgClose, width: 24, height: 24, scale: 0.75, color: "alert" }));
        const success = (react_1.default.createElement(polymesh_ui_1.IconOutlined, { Asset: polymesh_ui_1.icons.SvgCheckmark, width: 24, height: 24, scale: 0.75, color: "success" }));
        if (isApi && (hasErrors || failure))
            return fail;
        if (isApi && isFinalized && !hasErrors)
            return success;
        switch (status) {
            case types_1.TransactionStatus.Idle:
                return (react_1.default.createElement(polymesh_ui_1.Icon, { Asset: polymesh_ui_1.icons.SvgClock, width: 24, height: 24, color: "placeholder" }));
            case types_1.TransactionStatus.Unapproved:
                return (react_1.default.createElement(polymesh_ui_1.Icon, { Asset: polymesh_ui_1.icons.SvgClock, width: 24, height: 24, color: "brandMain" }));
            case types_1.TransactionStatus.Running:
                return (react_1.default.createElement(polymesh_ui_1.Icon, { Asset: polymesh_ui_1.icons.SvgClock, width: 24, height: 24, color: "brandMain" }));
            case types_1.TransactionStatus.Failed:
                return fail;
            case types_1.TransactionStatus.Succeeded:
                return success;
        }
    };
    const getText = ({ text, tag, status, }) => {
        const fail = (react_1.default.createElement(react_1.default.Fragment, null,
            t(`${tag}.${text}.${types_1.TransactionStatus.Failed}`),
            failUrl && text === 'statusText' && (react_1.default.createElement(react_1.default.Fragment, null,
                ' ',
                react_1.default.createElement(polymesh_ui_1.Link, { href: failUrl }, t('General.learnMore'))))));
        if (isApi && (hasErrors || failure))
            return fail;
        const success = t(`${tag}.${text}.${types_1.TransactionStatus.Succeeded}`);
        if (isApi && isFinalized && !hasErrors)
            return success;
        return t(`${tag}.${text}.${status}`);
    };
    const renderTransactions = (txs) => txs === null || txs === void 0 ? void 0 : txs.map((tx, index) => {
        return (react_1.default.createElement(polymesh_ui_1.Box, { key: index, mb: "m" },
            react_1.default.createElement(polymesh_ui_1.Box, { mb: "s" },
                react_1.default.createElement(polymesh_ui_1.Text, { variant: "b2m", color: "gray.2" }, getText({
                    text: 'title',
                    tag: tx.tag,
                    status: tx.status || types_1.TransactionStatus.Idle,
                }))),
            tx.txHash && (react_1.default.createElement(polymesh_ui_1.TransactionHash, { hash: tx.txHash, link: `${explorerUrl}/transaction/${tx.txHash}`, copyLink: true, size: 55, flexProps: {
                    flexDirection: 'row',
                    justifyContent: 'flex-start',
                    mb: 'm',
                }, buttonProps: {
                    copyId: 'queue_modal_txhash_copy_button',
                    linkId: 'queue_modal_txhash_link_button',
                } })),
            react_1.default.createElement(polymesh_ui_1.Box, { borderRadius: "3", borderColor: "gray.4", border: "1px solid", borderWidth: "1px", mb: "4", p: "m", width: 456 },
                react_1.default.createElement(polymesh_ui_1.Flex, { flexDirection: 1 },
                    react_1.default.createElement(polymesh_ui_1.Box, null, renderStatusIcon(tx.status || types_1.TransactionStatus.Idle)),
                    react_1.default.createElement(polymesh_ui_1.Box, { mx: "s" },
                        react_1.default.createElement(polymesh_ui_1.Text, { variant: "b2m", color: "gray.2" }, getText({
                            text: 'statusText',
                            tag: tx.tag,
                            status: tx.status || types_1.TransactionStatus.Idle,
                        })))))));
    });
    react_1.useEffect(() => {
        const doneTxs = transactions.filter((tx) => !!tx.txHash);
        if (doneTxs.length) {
            setCanDismiss(true);
        }
        else {
            setCanDismiss(false);
        }
    }, [transactions]);
    react_1.useEffect(() => {
        if (confirmationStatus === definition_1.ConfirmationStatus.Done) {
            setIsOpen(true);
        }
    }, [confirmationStatus]);
    const onDone = () => {
        let result;
        if (!failure) {
            result = definition_1.TransactionQueueResult.Success;
            if (hasErrors) {
                result = definition_1.TransactionQueueResult.Partial;
            }
            else {
                result = definition_1.TransactionQueueResult.Success;
            }
        }
        else {
            result = definition_1.TransactionQueueResult.Failure;
            // if (isAsync) dispatch(asyncActions.setAsync(false));
        }
        setTxHistory([...(signingPackage ? [signingPackage] : []), ...txHistory]);
        dispatch(transactionActions.transactionsCompleted({
            result,
            queue: txQueue,
            apiQueue,
            id: signingPackageId,
        }));
        dispatch(transactionActions.setConfirmationStatus(definition_1.ConfirmationStatus.None));
        if (onCompleted) {
            onCompleted(result, isApi ? apiQueue : txQueue === null || txQueue === void 0 ? void 0 : txQueue.transactions);
        }
        // const txTag = isApi ? queueType : txQueue?.transactions?.[0]?.tag;
        // heap.click({ id: `${txTag}_queue_modal_done_button` });
    };
    react_1.useEffect(() => {
        if (isFinalized) {
            setIsOpen(false);
            onDone();
        }
    }, [isFinalized]); // eslint-disable-line
    const renderHeader = () => (react_1.default.createElement(react_1.default.Fragment, null,
        react_1.default.createElement(polymesh_ui_1.Box, null,
            !isFinalized && react_1.default.createElement(polymesh_ui_1.LoadingDots, null),
            isFinalized && (react_1.default.createElement(polymesh_ui_1.IconCircled, { Asset: failure ? polymesh_ui_1.icons.SvgClose : polymesh_ui_1.icons.SvgCheckmark, width: 48, height: 48, scale: 0.75, bg: !isFinalized ? 'brandLightest' : failure ? 'red.1' : 'green.2', color: !isFinalized ? 'brandMain' : failure ? 'alert' : 'success' }))),
        react_1.default.createElement(polymesh_ui_1.Box, { ml: "m" },
            react_1.default.createElement(polymesh_ui_1.Heading, { variant: "h5" }, getText({
                text: 'heading',
                tag: signingPackageId,
                status: getQueueStatus(),
            })))));
    // const onModalDestroyed = () => {
    //   setIsFinalized(false);
    //   setHasErrors(false);
    //   setFailure(false);
    //   setQueue([]);
    //   dispatch(transactionActions.clearTransactions());
    //   // Callback
    //   if (onCompleted) {
    //     let result: TransactionQueueResult;
    //     if (!failure) {
    //       result = TransactionQueueResult.Success;
    //       if (hasErrors) {
    //         result = TransactionQueueResult.Partial;
    //       } else {
    //         result = TransactionQueueResult.Success;
    //       }
    //     } else {
    //       result = TransactionQueueResult.Failure;
    //       if (isAsync) dispatch(asyncActions.setAsync(false));
    //     }
    //     onCompleted(result, isApi ? apiQueue : txQueue?.transactions);
    //   }
    // };
    const toggleDrawer = () => {
        setIsOpen(!isOpen);
    };
    const dismiss = () => {
        setIsOpen(false);
        // if (onCompleted) {
        //   onCompleted(
        //     TransactionQueueResult.Success,
        //     isApi ? apiQueue : txQueue?.transactions
        //   );
        // }
    };
    const isOngoing = !isFinalized && (txQueue || apiQueue);
    return (react_1.default.createElement(react_1.default.Fragment, null,
        react_1.default.createElement(polymesh_ui_1.Button, { style: {
                position: 'fixed',
                top: 3,
                left: -10,
                padding: '0 0 0 10px',
                width: 50,
                minWidth: 'unset',
                zIndex: 9999,
            }, onClick: toggleDrawer },
            react_1.default.createElement(polymesh_ui_1.Icon, { Asset: polymesh_ui_1.icons.SvgFormatListBulleted, width: 22, height: 22 })),
        react_1.default.createElement(polymesh_ui_1.ModalDrawer, { isCloseable: false, isOpen: isOpen },
            react_1.default.createElement(polymesh_ui_1.Modal.Body, { width: 500, height: "100%" },
                !isOngoing && !txHistory.length && (react_1.default.createElement(polymesh_ui_1.Box, { mt: "xl" },
                    react_1.default.createElement(polymesh_ui_1.Text, { variant: "b1m", color: "gray.1" }, "No transactions"))),
                isOngoing && (react_1.default.createElement(react_1.default.Fragment, null,
                    react_1.default.createElement(polymesh_ui_1.Flex, { my: "l" }, renderHeader()),
                    renderTransactions(transactions),
                    react_1.default.createElement(polymesh_ui_1.Flex, { justifyContent: "flex-end", mt: "l", mb: "xl" }, canDismiss && (react_1.default.createElement(polymesh_ui_1.ButtonLarge, { onClick: dismiss }, "Finalize in background"))))),
                isOngoing && !!txHistory.length && react_1.default.createElement(polymesh_ui_1.Hr, null),
                !!txHistory.length && (react_1.default.createElement(polymesh_ui_1.CollapsableCard, { variant: "transparent", title: "Earlier Transactions", mt: "xl", defaultOpen: true }, txHistory.map((h) => {
                    var _a;
                    return (react_1.default.createElement(polymesh_ui_1.Box, { key: h.id, mb: "m" }, renderTransactions(((_a = h.queue) === null || _a === void 0 ? void 0 : _a.transactions) || h.apiQueue || [])));
                })))))));
};
//# sourceMappingURL=TxQueueModal.js.map