import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Spinner, StatusMessage } from '@inkjs/ui';
import { z } from 'zod';
import { option } from 'pastel';
import fs from 'fs/promises';
import { parse as parseTOML } from 'smol-toml';
import { zodSupportedNetwork } from '../../util/fetchSuperchainRegistryChainList.js';
import { useQueries } from '@tanstack/react-query';
import { getForgeArtifactQueryParams } from '../../queries/forgeArtifact.js';
import { resolveContractDeploymentParams, zodTarget, } from '../../util/resolveContractDeploymentParams.js';
import { Box, Text } from 'ink';
import { useEffect, useState } from 'react';
import { useConfig } from 'wagmi';
import { useMappingChainByIdentifier } from '../../queries/chainByIdentifier.js';
import { privateKeyToAccount } from 'viem/accounts';
import { computeDeploymentParams } from '../../actions/deploy-create2/computeDeploymentParams.js';
import { useMutation, useQuery } from '@tanstack/react-query';
import { ChooseExecutionOption, } from '../../components/ChooseExecutionOption.js';
import { VerifyCommandInner } from '../verify.js';
import { deployCreate2 } from '../../actions/deploy-create2/deployCreate2.js';
import { sendAllTransactionTasksWithPrivateKeyAccount, sendAllTransactionTasksWithCustomWalletRpc, } from '../../actions/deploy-create2/sendAllTransactionTasks.js';
import { getSponsoredSenderWalletRpcUrl } from '../../util/sponsoredSender.js';
import { zodPrivateKey } from '../../util/schemas.js';
import { useChecksForChainsForContracts } from '../../actions/deploy-create2/hooks/useChecksForChainsForContracts.js';
import { DeployStatus } from '../../actions/deploy-create2/components/DeployStatus.js';
const zodContractConfig = z
    .object({
    id: z.string().optional(),
    salt: z.string(),
    constructor_args: z.array(z.any()).optional(),
    forge_artifact_path: z.string(),
})
    .transform(params => {
    return {
        id: params.id,
        salt: params.salt,
        constructorArgs: params.constructor_args,
        forgeArtifactPath: params.forge_artifact_path,
    };
});
const zodCreate2ManyConfig = z.object({
    chains: z.array(z.string()),
    network: zodSupportedNetwork,
    contracts: z.array(zodContractConfig),
});
const zodDeployCreate2ManyCommandOptions = z.object({
    toml: z.string().describe(option({
        description: 'Path to a TOML file to use as a configuration',
        alias: 't',
    })),
    privateKey: zodPrivateKey.optional().describe(option({
        description: 'Signer private key',
        alias: 'pk',
    })),
    verify: z
        .boolean()
        .optional()
        .describe(option({
        description: 'Verify contracts after deployment',
        alias: 'v',
    })),
});
const toUnresolvedConstructorArg = (arg) => {
    // handle {{TokenERC20.address}} or {{TokenERC20.someOtherProp}}
    // => { type: 'reference', id: 'TokenERC20', target: 'address' | 'someOtherProp' }
    if (typeof arg === 'string') {
        const match = arg.match(/^{{([^.]+)\.([^}]+)}}$/);
        if (match) {
            if (match.length !== 3) {
                throw new Error('Invalid reference format');
            }
            const target = zodTarget.safeParse(match[2]);
            if (target.success === false) {
                throw new Error('Invalid target');
            }
            return {
                type: 'reference',
                id: match[1],
                target: target.data,
            };
        }
    }
    return { type: 'value', value: arg };
};
const toUnresolvedContractDeploymentParams = (config, forgeArtifact) => {
    return {
        id: config.id || config.forgeArtifactPath, // default to the forge artifact path if no id is provided
        salt: config.salt,
        constructorArgs: config.constructorArgs?.map(toUnresolvedConstructorArg),
        forgeArtifact,
    };
};
const DeployCreate2ManyCommand = ({ options, }) => {
    const { data: config, isLoading: isConfigLoading, error, } = useQuery({
        queryKey: ['read-toml', options.toml],
        queryFn: async () => {
            const raw = await fs.readFile(options.toml, { encoding: 'utf-8' });
            const parsedToml = parseTOML(raw);
            const parsed = zodCreate2ManyConfig.safeParse(parsedToml);
            if (parsed.success === false) {
                throw new Error('Invalid config file');
            }
            return parsed.data;
        },
    });
    if (isConfigLoading) {
        return _jsx(Spinner, {});
    }
    if (error) {
        return _jsx(StatusMessage, { variant: "error", children: error.message });
    }
    if (!config) {
        return _jsx(StatusMessage, { variant: "error", children: "No config found" });
    }
    return _jsx(DeployCreate2ManyWithConfig, { config: config, options: options });
};
// TODO: Make this wayyy simpler or split out into multiple components
const DeployCreate2ManyWithConfig = ({ config, options, }) => {
    const forgeArtifactQueryResults = useQueries({
        queries: config.contracts.map(contract => getForgeArtifactQueryParams(contract.forgeArtifactPath)),
    });
    const { data: chainByIdentifier, isLoading: isChainByIdentifierLoading } = useMappingChainByIdentifier();
    if (isChainByIdentifierLoading || !chainByIdentifier) {
        return _jsx(Spinner, { label: "Loading chain configurations..." });
    }
    if (forgeArtifactQueryResults.some(result => result.isLoading)) {
        return _jsx(Spinner, { label: "Loading forge artifacts..." });
    }
    if (forgeArtifactQueryResults.some(result => result.error)) {
        return (_jsx(StatusMessage, { variant: "error", children: forgeArtifactQueryResults.find(result => result.error)?.error?.message }));
    }
    if (forgeArtifactQueryResults.some(result => result.data === undefined)) {
        return (_jsx(StatusMessage, { variant: "error", children: "Error loading forge artifacts" }));
    }
    const unresolvedContractDeploymentParams = forgeArtifactQueryResults.map((result, index) => toUnresolvedContractDeploymentParams(config.contracts[index], result.data));
    const resolvedContractDeploymentParams = resolveContractDeploymentParams(unresolvedContractDeploymentParams);
    const flattenedChains = config.chains.flatMap(chain => chain.split(','));
    const chains = flattenedChains.map(x => {
        const chain = chainByIdentifier[`${config.network}/${x}`];
        if (!chain) {
            throw new Error(`Chain ${`${config.network}/${x}`} not found`);
        }
        return chain;
    });
    const deploymentParams = config.contracts.map((contract, index) => {
        const { forgeArtifact, constructorArgs, salt } = resolvedContractDeploymentParams[index];
        const intent = {
            chains,
            forgeArtifactPath: contract.forgeArtifactPath,
            forgeArtifact,
            constructorArgs,
            salt,
        };
        return {
            intent,
            computedParams: computeDeploymentParams(intent),
        };
    });
    return (_jsx(DeployCreate2ManyCommandInner, { deploymentParams: deploymentParams, options: options }));
};
const DeployCreate2ManyCommandInner = ({ deploymentParams, options, }) => {
    const [executionOption, setExecutionOption] = useState(options.privateKey
        ? { type: 'privateKey', privateKey: options.privateKey }
        : null);
    const { chainIdSetByAddress, hasDeployableChains } = useChecksForChainsForContracts(deploymentParams);
    const wagmiConfig = useConfig();
    const { mutate, data } = useMutation({
        mutationFn: () => {
            if (!chainIdSetByAddress) {
                throw new Error('No chains to deploy to');
            }
            return Promise.all(deploymentParams.map(async ({ intent, computedParams }) => {
                const { forgeArtifactPath, constructorArgs, chains } = intent;
                const { deterministicAddress, initCode, baseSalt } = computedParams;
                const chainIdsToDeployTo = chainIdSetByAddress[deterministicAddress];
                if (chainIdsToDeployTo.size === 0) {
                    return;
                }
                return await deployCreate2({
                    wagmiConfig,
                    deterministicAddress,
                    initCode,
                    baseSalt,
                    chains: chains.filter(chain => chainIdsToDeployTo.has(chain.id)),
                    foundryArtifactPath: forgeArtifactPath,
                    contractArguments: constructorArgs || [],
                    account: options.privateKey
                        ? privateKeyToAccount(options.privateKey)
                        : undefined,
                });
            }));
        },
    });
    useEffect(() => {
        if (!chainIdSetByAddress)
            return;
        mutate();
    }, [
        Object.keys(chainIdSetByAddress || {})
            .map(key => {
            return Array.from((chainIdSetByAddress?.[key] || []).values()).join(',');
        })
            .join(','),
    ]);
    return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [deploymentParams.map(({ intent, computedParams }, i) => (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Text, { bold: true, children: ["Deploy ", i + 1, ":", ' ', _jsx(Text, { color: "cyan", children: intent.forgeArtifactPath })] }), _jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [_jsx(Text, {}), _jsxs(Text, { children: ["Salt: ", _jsx(Text, { color: "magenta", children: intent.salt })] }), intent.constructorArgs && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Constructor Arguments:" }), _jsx(Text, { color: "cyan", children: intent.constructorArgs?.join(', ') })] })), _jsx(Box, { children: _jsxs(Text, { children: ["Address:", ' ', _jsx(Text, { color: "yellow", bold: true, children: computedParams.deterministicAddress })] }) })] })] }), _jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: intent.chains.map(chain => (_jsxs(Text, { bold: true, color: "blue", children: [chain.name, ":"] }, chain.id))) }), _jsx(Box, { flexDirection: "column", children: intent.chains.map(chain => (_jsx(DeployStatus, { chain: chain, initCode: computedParams.initCode, baseSalt: computedParams.baseSalt, deterministicAddress: computedParams.deterministicAddress, executionOption: executionOption }, chain.id))) })] }) })] }, intent.forgeArtifactPath))), hasDeployableChains && !executionOption && (_jsx(Box, { children: _jsx(ChooseExecutionOption, { label: '🚀 Ready to deploy!', onSubmit: async (executionOption) => {
                        if (executionOption.type === 'privateKey') {
                            setExecutionOption(executionOption);
                            await sendAllTransactionTasksWithPrivateKeyAccount(privateKeyToAccount(executionOption.privateKey));
                        }
                        else if (executionOption.type === 'sponsoredSender') {
                            setExecutionOption(executionOption);
                            await sendAllTransactionTasksWithCustomWalletRpc(chainId => getSponsoredSenderWalletRpcUrl(executionOption.apiKey, chainId));
                        }
                        else {
                            setExecutionOption(executionOption);
                        }
                    } }) })), data && (_jsx(CompletedOrVerify, { shouldVerify: !!options.verify, deploymentParams: deploymentParams }))] }));
};
const CompletedOrVerify = ({ shouldVerify, deploymentParams, }) => {
    if (!shouldVerify) {
        // TODO: hacky way to quit until we remove pastel
        setTimeout(() => {
            process.exit(0);
        }, 1);
        return (_jsx(Box, { children: _jsx(Text, { children: "Contracts are successfully deployed to all chains" }) }));
    }
    return (_jsx(Box, { flexDirection: "column", gap: 1, children: deploymentParams.map((deploymentParam, index) => (_jsx(VerifyCommandInner, { chains: deploymentParam.intent.chains, forgeArtifactPath: deploymentParam.intent.forgeArtifactPath, contractAddress: deploymentParam.computedParams.deterministicAddress, forgeArtifact: deploymentParam.intent.forgeArtifact, index: index }, deploymentParam.computedParams.deterministicAddress))) }));
};
export default DeployCreate2ManyCommand;
export const options = zodDeployCreate2ManyCommandOptions;
