//@ts-ignore
import BrowserOnly from "@docusaurus/BrowserOnly";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { useWallet } from "@solana/wallet-adapter-react";
import { useChatSdk } from "@strata-foundation/chat-ui";
import { WalletModalProvider, WalletMultiButton, } from "@solana/wallet-adapter-react-ui";
import { Connection, PublicKey } from "@solana/web3.js";
import { useProvider, useStrataSdks, useEndpoint, } from "@strata-foundation/react";
import BN from "bn.js";
import clsx from "clsx";
import { parse } from "esprima";
import React, { useEffect, useMemo, useState } from "react";
import { FaPlay } from "react-icons/fa";
import { useVariablesContext } from "../Root/variables";
//@ts-ignore
import styles from "./styles.module.css";
import { clusterApiUrl } from "@solana/web3.js";
import { useMarketplaceSdk } from "@strata-foundation/marketplace-ui";
function BrowserOnlyReactJson(props) {
    return (React.createElement(BrowserOnly, { fallback: React.createElement("div", null, "...") }, () => {
        const Component = require("react-json-view").default;
        return React.createElement(Component, { ...props });
    }));
}
function wrapAndCollectVars(code, injectedVars) {
    const wrapped = `(async function() {
    ${code}
  }())`;
    const declarations = parse(wrapped)
        .body[0].expression.callee.body.body.filter(({ type }) => type === "VariableDeclaration")
        .flatMap(({ declarations }) => declarations)
        .map(({ id }) => id);
    const variables = declarations.flatMap(({ type, name, properties }) => {
        if (type === "Identifier") {
            return [name];
        }
        else if (type === "ObjectPattern") {
            return properties.map(({ key }) => key.name);
        }
        return [];
    });
    return `
(async function() {
  var { ${Object.keys(injectedVars).join(", ")} } = injectedVars;
  ${code}
  ${variables.map((variable) => `vars.${variable} = ${variable};`).join("\n")}
}())
  `;
}
// Turn all BN into base 10 numbers as strings
export function recursiveTransformBN(args, seen = new Map()) {
    if (seen.has(args)) {
        return seen.get(args);
    }
    const ret = Object.entries(args).reduce((acc, [key, value]) => {
        if (value instanceof BN) {
            acc[key] = value.toString(10);
        }
        else if (value instanceof PublicKey) {
            // @ts-ignore
            acc[key] = value.toBase58();
        }
        else if (value && value._bn) {
            acc[key] = new PublicKey(new BN(value._bn, "hex")).toBase58();
        }
        else if (typeof value === "object" && value !== null) {
            seen.set(args, null);
            acc[key] = recursiveTransformBN(value, seen);
        }
        else {
            acc[key] = value;
        }
        return acc;
    }, {});
    seen.set(args, ret);
    return ret;
}
const AsyncButton = ({ code, scope, name, deps, allowMainnet = false }) => {
    const [loading, setLoading] = useState(false);
    const [runningThisCommand, setRunningThisCommand] = useState(false);
    const { register, execWithDeps } = useVariablesContext();
    const [variables, setVariables] = useState(null);
    const [error, setError] = useState();
    const { connected, publicKey } = useWallet();
    const sdks = useStrataSdks();
    const { marketplaceSdk } = useMarketplaceSdk();
    const { provider } = useProvider();
    const { chatSdk } = useChatSdk();
    const { endpoint, setClusterOrEndpoint } = useEndpoint();
    var vars = {}; // Outer variable, not stored.
    const exec = useMemo(() => {
        async function execInner(globalVariables) {
            setRunningThisCommand(true);
            const connection = new Connection(clusterApiUrl(WalletAdapterNetwork.Devnet));
            try {
                try {
                    const walletAcct = publicKey && (await connection.getAccountInfo(publicKey));
                    if (!walletAcct || walletAcct.lamports < 500000000) {
                        publicKey &&
                            (await connection.requestAirdrop(publicKey, 1000000000));
                    }
                }
                catch (e) {
                    // ignore. If we can't airdrop it's probably mainnet
                }
                const injectedVars = {
                    provider,
                    ...sdks,
                    marketplaceSdk,
                    chatSdk,
                    publicKey,
                    ...scope,
                    ...globalVariables,
                };
                await eval(wrapAndCollectVars(code, injectedVars));
                setVariables(vars);
                return {
                    ...globalVariables,
                    ...vars,
                };
            }
            finally {
                setRunningThisCommand(false);
            }
        }
        return execInner;
    }, [
        chatSdk,
        marketplaceSdk,
        provider,
        publicKey,
        sdks.tokenCollectiveSdk,
        sdks.tokenMetadataSdk,
        sdks.tokenBondingSdk,
    ]);
    async function wrappedExecWithDeps() {
        setError(undefined);
        setLoading(true);
        try {
            await execWithDeps(name);
        }
        catch (e) {
            setError(e);
        }
        finally {
            setLoading(false);
        }
    }
    useEffect(() => {
        register(name, deps.filter(Boolean), exec);
    }, [publicKey, ...Object.values(sdks), marketplaceSdk, chatSdk]);
    useEffect(() => {
        if (error) {
            console.error(error);
        }
    }, [error]);
    const fullLoading = loading || !sdks.tokenBondingSdk || !sdks.tokenCollectiveSdk;
    if (!(endpoint.includes("devnet") || endpoint.includes("localhost")) &&
        !allowMainnet) {
        return (React.createElement("div", { className: styles.container },
            React.createElement("button", { onClick: () => {
                    setClusterOrEndpoint(WalletAdapterNetwork.Devnet);
                }, className: "white button button--primary" }, "Switch to Devnet")));
    }
    return (React.createElement("div", { className: styles.container },
        (!sdks.tokenBondingSdk || !sdks.tokenCollectiveSdk) && (React.createElement("div", null, "Loading SDK...")),
        loading && !runningThisCommand && (React.createElement("div", null, "Running previous commands...")),
        loading && runningThisCommand && React.createElement("div", null, "Loading..."),
        error ? (React.createElement("div", null, error.toString())) : fullLoading ? undefined : (React.createElement(BrowserOnlyReactJson, { theme: "bright:inverted", collapsed: 1, displayDataTypes: false, name: false, src: recursiveTransformBN(variables || {}) })),
        connected && (React.createElement("button", { disabled: fullLoading, className: clsx(styles.runButton, "white button button--primary"), onClick: wrappedExecWithDeps },
            React.createElement(FaPlay, { className: styles.buttonIcon }),
            " Run")),
        !connected && (React.createElement(React.Fragment, null,
            React.createElement(WalletModalProvider, null,
                React.createElement(WalletMultiButton, null))))));
};
export default (props) => (React.createElement(BrowserOnly, { fallback: React.createElement("div", null, "...") }, () => React.createElement(AsyncButton, { ...props })));
//# sourceMappingURL=AsyncButton.js.map