import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { Box, Text } from 'ink';
import { useEffect, useState } from 'react';
import { Spinner, Badge } from '@inkjs/ui';
import { encodeFunctionData } from 'viem';
import { useConfig, useWaitForTransactionReceipt, useWriteContract } from 'wagmi';
import { useForgeArtifact } from '../queries/forgeArtifact.js';
import { CREATEX_ADDRESS, createXABI } from '../contracts/createx/constants.js';
import { useMappingChainByIdentifier } from '../queries/chainByIdentifier.js';
import { privateKeyToAccount } from 'viem/accounts';
import { useTransactionTaskStore } from '../stores/transactionTaskStore.js';
import { createTransactionTaskId } from '../transaction-task/transactionTask.js';
import { getBlockExplorerAddressLink } from '../utils/blockExplorer.js';
import { getDeployCreate2Params } from './getDeployCreate2Params.js';
import { useQuery } from '@tanstack/react-query';
import { preVerificationCheckQueryOptions } from './preVerificationCheckQuery.js';
import { simulationCheckQueryOptions } from './simulationCheckQuery.js';
import { useChecksForChains } from './useChecksForChains.js';
import { ChooseExecutionOption, } from './ChooseExecutionOption.js';
import { useCodeForChains } from './useCodeForChains.js';
import { useRefetchCodeOnReceipt } from './useRefetchCodeOnReceipt.js';
import { VerifyCommandInner } from '../commands/verify.js';
// Prepares any required data or loading state if waiting
export const DeployCreate2Command = ({ options, }) => {
    const { data: chainByIdentifier, isLoading: isChainByIdentifierLoading } = useMappingChainByIdentifier();
    const { data: forgeArtifact, isLoading: isForgeArtifactLoading } = useForgeArtifact(options.forgeArtifactPath);
    if (isForgeArtifactLoading ||
        !forgeArtifact ||
        isChainByIdentifierLoading ||
        !chainByIdentifier) {
        return _jsx(Spinner, {});
    }
    const chains = options.chains.map(chain => chainByIdentifier[`${options.network}/${chain}`]);
    return (_jsx(DeployCreate2CommandInner, { chains: chains, forgeArtifact: forgeArtifact, options: options }));
};
const DeployCreate2CommandInner = ({ chains, forgeArtifact, options, }) => {
    const [executionOption, setExecutionOption] = useState(options.privateKey
        ? { type: 'privateKey', privateKey: options.privateKey }
        : null);
    const { initCode, deterministicAddress, baseSalt } = getDeployCreate2Params({
        forgeArtifact,
        constructorArgs: options.constructorArgs,
        salt: options.salt,
    });
    const { isDeployedToAllChains } = useCodeForChains(deterministicAddress, chains.map(chain => chain.id));
    const { chainsToDeployTo } = useChecksForChains({
        deterministicAddress,
        initCode,
        baseSalt,
        chainIds: chains.map(chain => chain.id),
    });
    return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Text, { bold: true, underline: true, children: "Deployments" }), _jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [_jsxs(Text, { children: ["Contract: ", _jsx(Text, { color: "cyan", children: options.forgeArtifactPath })] }), _jsxs(Text, { children: ["Network: ", _jsx(Text, { color: "blue", children: options.network })] }), _jsxs(Text, { children: ["Target Chains:", ' ', _jsx(Text, { color: "green", children: chains.map(chain => chain.name).join(', ') })] }), _jsxs(Text, { children: ["Salt: ", _jsx(Text, { color: "magenta", children: baseSalt })] }), options.constructorArgs && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: "Constructor Arguments:" }), _jsx(Text, { color: "cyan", children: options.constructorArgs.split(',').join(', ') })] })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Address:", ' ', _jsx(Text, { color: "yellow", bold: true, children: deterministicAddress })] }) })] })] }), _jsx(Box, { flexDirection: "column", paddingX: 2, children: _jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexDirection: "column", marginRight: 2, children: chains.map(chain => (_jsxs(Text, { bold: true, color: "blue", children: [chain.name, ":"] }, chain.id))) }), _jsx(Box, { flexDirection: "column", children: chains.map(chain => (_jsx(DeployStatus, { chain: chain, initCode: initCode, baseSalt: baseSalt, deterministicAddress: deterministicAddress, executionOption: executionOption }, chain.id))) })] }) }), chainsToDeployTo && chainsToDeployTo.length > 0 && !executionOption && (_jsx(Box, { children: _jsx(ChooseExecutionOption, { onSubmit: setExecutionOption }) })), isDeployedToAllChains && (_jsx(CompletedOrVerify, { shouldVerify: !!options.verify, chains: chains, forgeArtifactPath: options.forgeArtifactPath, contractAddress: deterministicAddress, forgeArtifact: forgeArtifact }))] }));
};
const CompletedOrVerify = ({ shouldVerify, chains, forgeArtifactPath, contractAddress, forgeArtifact, }) => {
    if (!shouldVerify) {
        return (_jsx(Box, { children: _jsx(Text, { children: "Contract is successfully deployed to all chains" }) }));
    }
    return (_jsx(VerifyCommandInner, { chains: chains, forgeArtifactPath: forgeArtifactPath, contractAddress: contractAddress, forgeArtifact: forgeArtifact }));
};
const DeployStatus = ({ chain, initCode, baseSalt, deterministicAddress, executionOption, }) => {
    const wagmiConfig = useConfig();
    const { data: preVerificationCheckData, isLoading: isPreVerificationCheckLoading, error: preVerificationCheckError, } = useQuery({
        ...preVerificationCheckQueryOptions(wagmiConfig, {
            deterministicAddress,
            initCode,
            baseSalt,
            chainId: chain.id,
        }),
    });
    const { data: simulationData, isLoading: isSimulationLoading, error: simulationError, } = useQuery({
        ...simulationCheckQueryOptions(wagmiConfig, {
            deterministicAddress,
            initCode,
            baseSalt,
            chainId: chain.id,
        }),
    });
    if (preVerificationCheckError) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "red", children: "Error" }), _jsxs(Text, { children: ["Pre-verification check failed:", ' ', preVerificationCheckError.message.split('\n')[0]] })] }));
    }
    if (isPreVerificationCheckLoading || !preVerificationCheckData) {
        return _jsx(Spinner, { label: "Checking if contract is already deployed" });
    }
    if (preVerificationCheckData.isAlreadyDeployed) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "green", children: "Deployed" }), _jsx(Text, { children: "Contract is already deployed" }), _jsx(Text, { children: getBlockExplorerAddressLink(chain, deterministicAddress) })] }));
    }
    if (simulationError) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "red", children: "Error" }), _jsxs(Text, { children: ["Simulation check failed: ", simulationError.message.split('\n')[0]] })] }));
    }
    if (isSimulationLoading || !simulationData) {
        return (_jsx(Spinner, { label: "Simulating deployment to check for address mismatch" }));
    }
    if (!simulationData.isAddressSameAsExpected) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "red", children: "Failed" }), _jsx(Text, { children: "Address mismatch" })] }));
    }
    if (!executionOption) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "blue", children: "Ready" }), _jsx(Text, { children: "Contract is ready to be deployed" })] }));
    }
    if (executionOption.type === 'privateKey') {
        return (_jsx(PrivateKeyExecution, { chain: chain, initCode: initCode, baseSalt: baseSalt, deterministicAddress: deterministicAddress, privateKey: executionOption.privateKey }));
    }
    return (_jsx(ExternalSignerExecution, { chain: chain, initCode: initCode, baseSalt: baseSalt, deterministicAddress: deterministicAddress }));
};
const PrivateKeyExecution = ({ chain, initCode, baseSalt, deterministicAddress, privateKey, }) => {
    const { writeContract, isPending, error, data: transactionHash, } = useWriteContract();
    const { data: receipt, isLoading: isReceiptLoading } = useWaitForTransactionReceipt({
        hash: transactionHash,
        chainId: chain.id,
    });
    useRefetchCodeOnReceipt(deterministicAddress, chain.id, receipt);
    useEffect(() => {
        writeContract({
            abi: createXABI,
            account: privateKeyToAccount(privateKey),
            address: CREATEX_ADDRESS,
            chainId: chain.id,
            functionName: 'deployCreate2',
            args: [baseSalt, initCode],
        });
    }, []);
    if (isPending) {
        return _jsx(Spinner, { label: "Deploying contract" });
    }
    if (error) {
        return _jsxs(Text, { children: ["Error deploying contract: ", error.message] });
    }
    if (isReceiptLoading) {
        return _jsx(Spinner, { label: "Waiting for receipt" });
    }
    return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "green", children: "Deployed" }), _jsx(Text, { children: "Contract successfully deployed" }), _jsx(Text, { children: getBlockExplorerAddressLink(chain, deterministicAddress) })] }));
};
const ExternalSignerExecution = ({ chain, initCode, baseSalt, deterministicAddress, }) => {
    const encodedData = encodeFunctionData({
        abi: createXABI,
        args: [baseSalt, initCode],
        functionName: 'deployCreate2',
    });
    const { createTask, taskEntryById } = useTransactionTaskStore();
    const taskId = createTransactionTaskId({
        chainId: chain.id,
        to: CREATEX_ADDRESS,
        data: encodedData,
    });
    useEffect(() => {
        createTask({
            chainId: chain.id,
            to: CREATEX_ADDRESS,
            data: encodedData,
        });
    }, []);
    const { data: receipt, isLoading: isReceiptLoading } = useWaitForTransactionReceipt({
        hash: taskEntryById[taskId]?.hash,
        chainId: chain.id,
    });
    useRefetchCodeOnReceipt(deterministicAddress, chain.id, receipt);
    if (isReceiptLoading) {
        return _jsx(Spinner, { label: "Waiting for receipt" });
    }
    if (!taskEntryById[taskId]?.hash) {
        return (_jsxs(Box, { gap: 1, children: [_jsx(Spinner, { label: "Waiting for signature..." }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { children: "Send the transaction at " }), _jsx(Text, { color: "cyan", bold: true, children: "http://localhost:3000" })] })] }));
    }
    return (_jsxs(Box, { gap: 1, children: [_jsx(Badge, { color: "green", children: "Deployed" }), _jsx(Text, { children: "Contract successfully deployed" }), _jsx(Text, { children: getBlockExplorerAddressLink(chain, deterministicAddress) }), "P"] }));
};
