import { Resource } from "../resource.js";
import { createCdpClient } from "./client.js";
import { validateAccountName } from "./utils.js";
/**
 * Manages EVM EOA (Externally Owned Accounts) on Coinbase Developer Platform.
 *
 * @example
 * ## Create a new EVM account
 *
 * ```ts
 * const account = await EvmAccount("my-account", {
 *   name: "my-account"
 * });
 *
 * console.log("Account address:", account.address);
 * ```
 *
 * @example
 * ## Create account with funding metadata
 *
 * Declare what tokens this account needs for development
 *
 * ```ts
 * const account = await EvmAccount("test-account", {
 *   name: "test-account",
 *   faucet: {
 *     "base-sepolia": ["eth", "usdc"],
 *     "ethereum-sepolia": ["eth"]
 *   }
 * });
 *
 * // Account automatically requests tokens on creation
 * // The faucet script can be used for bulk funding operations
 * ```
 *
 * @example
 * ## Import an existing account
 *
 * Import an existing EVM account using a private key
 *
 * ```ts
 * const account = await EvmAccount("imported-account", {
 *   name: "imported-account",
 *   privateKey: alchemy.secret(process.env.COINBASE_PRIVATE_KEY)
 * });
 * ```
 *
 * @example
 * ## Adopt an existing account
 *
 * Use an existing account with the same name if it exists
 *
 * ```ts
 * const account = await EvmAccount("my-account", {
 *   name: "existing-account",
 *   adopt: true // Uses existing account if it exists
 * });
 * ```
 */
export const EvmAccount = Resource("coinbase::evm-account", async function (_id, props) {
    // Validate account name format
    validateAccountName(props.name);
    // Initialize CDP client with credentials from props or environment
    const cdp = await createCdpClient({
        apiKeyId: props.apiKeyId,
        apiKeySecret: props.apiKeySecret,
        walletSecret: props.walletSecret,
    });
    // Handle update phase
    if (this.phase === "update" && this.output) {
        // Only name can be updated
        if (props.name !== this.output.name) {
            // CDP SDK supports updating account names via updateAccount
            await cdp.evm.updateAccount({
                address: this.output.address,
                update: {
                    name: props.name,
                },
            });
            return {
                ...this.output,
                name: props.name, // Use the requested name
                faucet: props.faucet,
            };
        }
        // Update faucet configuration if changed
        if (JSON.stringify(props.faucet) !== JSON.stringify(this.output.faucet)) {
            // Only fund NEW combinations that weren't in the previous config
            try {
                // Get all old combinations
                const oldCombinations = new Set(Object.entries(this.output.faucet ?? {}).flatMap(([network, tokens]) => tokens.map((token) => `${network}:${token}`)));
                // Get all new combinations
                const newCombinations = Object.entries(props.faucet ?? {}).flatMap(([network, tokens]) => tokens.map((token) => ({
                    network: network,
                    token: token,
                    key: `${network}:${token}`,
                })));
                // Filter to only combinations that are actually new
                const toFund = newCombinations.filter(({ key }) => !oldCombinations.has(key));
                if (toFund.length > 0) {
                    await Promise.all(toFund.map(({ network, token }) => cdp.evm
                        .requestFaucet({
                        address: this.output.address,
                        network,
                        token,
                    })
                        .catch((err) => {
                        console.warn(`⚠️ Failed to request ${token} on ${network} for ${this.output.address}: ${err.message}`);
                        return null;
                    })));
                    console.log(`💧 Requested ${toFund.length} new faucet fund${toFund.length === 1 ? "" : "s"} for ${this.output.address}`);
                }
            }
            catch (error) {
                console.warn(`⚠️ Failed to request additional faucet funds for ${this.output.address}: ${error.message}`);
                // Continue - don't break the update
            }
            return {
                ...this.output,
                faucet: props.faucet,
            };
        }
        return this.output;
    }
    // Handle delete phase
    if (this.phase === "delete") {
        // CDP SDK doesn't support deleting accounts
        // Accounts remain in CDP but are no longer tracked by Alchemy
        console.log(`🗑️ Untracking EVM Account: ${this.output.address}`);
        return this.destroy();
    }
    let account;
    // Handle account creation/retrieval
    if (props.privateKey) {
        // Import account with private key
        // This is idempotent in CDP
        account = await cdp.evm.importAccount({
            privateKey: props.privateKey.unencrypted,
            name: props.name,
        });
    }
    else {
        // Check for adoption or use getOrCreate pattern
        const adopt = props.adopt ?? this.scope.adopt;
        if (adopt) {
            // With adoption, use getOrCreate which returns existing if it exists
            account = await cdp.evm.getOrCreateAccount({
                name: props.name,
            });
        }
        else {
            // Without adoption, we can attempt to create it.
            // It will throw if already exist
            try {
                account = await cdp.evm.createAccount({ name: props.name });
            }
            catch (error) {
                // Provide helpful error message if account already exists
                if (error.errorType === "already_exists") {
                    throw new Error(`Account with name '${props.name}' already exists. Use adopt: true to use the existing account.`);
                }
                // Rethrow other errors unchanged
                throw error;
            }
        }
    }
    // Fund the account if faucet configuration is provided
    if (props.faucet) {
        try {
            const combinations = Object.entries(props.faucet).flatMap(([network, tokens]) => tokens.map((token) => ({
                network: network,
                token: token,
            })));
            await Promise.all(combinations.map(({ network, token }) => cdp.evm
                .requestFaucet({
                address: account.address,
                network,
                token,
            })
                .catch((err) => {
                console.warn(`⚠️ Failed to request ${token} on ${network} for ${account.address}: ${err.message}`);
                return null;
            })));
            console.log(`💧 Requested faucet funds for ${account.address}`);
        }
        catch (error) {
            console.warn(`⚠️ Failed to request faucet funds for ${account.address}: ${error.message}`);
            // Continue - don't break account creation
        }
    }
    // Return account details
    return {
        name: props.name, // Always use the name from props for consistency
        address: account.address,
        faucet: props.faucet,
    };
});
//# sourceMappingURL=evm-account.js.map