import { Resource } from "../resource.js";
import { createCdpClient } from "./client.js";
import { validateAccountName } from "./utils.js";
/**
 * Manages ERC-4337 smart accounts on Coinbase Developer Platform.
 * Smart accounts enable gasless transactions and advanced features like batch operations.
 *
 * @example
 * ## Create a smart account with an EVM account owner
 *
 * ```ts
 * const owner = await EvmAccount("owner", {
 *   name: "owner-account"
 * });
 *
 * const smartAccount = await EvmSmartAccount("my-smart-account", {
 *   name: "my-smart-account",
 *   owner: owner
 * });
 *
 * console.log("Smart account address:", smartAccount.address);
 * ```
 *
 * @example
 * ## Create a smart account with existing owner
 *
 * ```ts
 * const smartAccount = await EvmSmartAccount("my-smart-account", {
 *   name: "my-smart-account",
 *   owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" // Owner address
 * });
 * ```
 *
 * @example
 * ## Smart account with inherited name
 *
 * When name is omitted, the smart account inherits the owner's name
 *
 * ```ts
 * const owner = await EvmAccount("owner", {
 *   name: "my-app-wallet"
 * });
 *
 * // Smart account will also be named "my-app-wallet" in CDP
 * const smartAccount = await EvmSmartAccount("smart", {
 *   owner: owner
 *   // name is omitted, inherits "my-app-wallet" from owner
 * });
 *
 * console.log(smartAccount.name); // "my-app-wallet"
 * ```
 *
 * @example
 * ## Adopt an existing smart account
 *
 * ```ts
 * const smartAccount = await EvmSmartAccount("my-smart-account", {
 *   name: "existing-smart-account",
 *   owner: ownerAccount, // or "0x..." address
 *   adopt: true // Uses existing smart account if it exists
 * });
 * ```
 */
export const EvmSmartAccount = Resource("coinbase::evm-smart-account", async function (_id, props) {
    // Initialize CDP client with credentials from props or environment
    const cdp = await createCdpClient({
        apiKeyId: props.apiKeyId,
        apiKeySecret: props.apiKeySecret,
        walletSecret: props.walletSecret,
    });
    // Get owner address
    const ownerAddress = typeof props.owner === "string" ? props.owner : props.owner.address;
    // Get owner account for CDP operations
    // When owner is an address string, we use it to get the account
    const ownerAccount = typeof props.owner === "string"
        ? await cdp.evm.getAccount({ address: props.owner })
        : await cdp.evm.getAccount({ address: props.owner.address });
    // If no name provided, inherit from owner
    // this mean that if you pass only { owner } the created smartAccount will be same name as the EOA owner
    // this to support https://github.com/base/account-sdk/blob/master/packages/account-sdk/src/interface/payment/charge.ts#L120
    let accountName = props.name ? props.name : ownerAccount.name;
    if (!accountName) {
        throw new Error(`Smart account requires a name. Either provide 'name' in props or ensure the owner account has a name. Owner address: ${ownerAddress}`);
    }
    // Validate account name format
    validateAccountName(accountName);
    // Handle update phase
    if (this.phase === "update" && this.output) {
        // Check if owner changed (which would require replacement)
        if (ownerAddress !== this.output.ownerAddress) {
            this.replace();
        }
        // Check if name changed
        if (accountName !== this.output.name) {
            await cdp.evm.updateSmartAccount({
                address: this.output.address,
                owner: ownerAccount,
                update: {
                    name: accountName,
                },
            });
            return {
                ...this.output,
                name: accountName,
                // faucet: props.faucet, not sure we need this?
            };
        }
        // 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 smart account ${this.output.address}: ${err.message}`);
                        return null;
                    })));
                    console.log(`💧 Requested ${toFund.length} new faucet fund${toFund.length === 1 ? "" : "s"} for smart account ${this.output.address}`);
                }
            }
            catch (error) {
                console.warn(`⚠️ Failed to request additional faucet funds for smart account ${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 Smart Account: ${this.output?.address}`);
        return this.destroy();
    }
    let smartAccount;
    // Check for adoption or use getOrCreate pattern
    const adopt = props.adopt ?? this.scope.adopt;
    if (adopt) {
        // With adoption, use getOrCreateSmartAccount which returns existing if it exists
        smartAccount = await cdp.evm.getOrCreateSmartAccount({
            name: accountName,
            owner: ownerAccount,
        });
    }
    else {
        // Without adoption, create a new smart account
        try {
            smartAccount = await cdp.evm.createSmartAccount({
                name: accountName,
                owner: ownerAccount,
            });
        }
        catch (error) {
            // Provide helpful error message if smart account already exists
            if (error.errorType === "already_exists") {
                throw new Error(`Smart account with name '${accountName}' already exists. Use adopt: true to use the existing smart account.`);
            }
            // Rethrow other errors unchanged
            throw error;
        }
    }
    // Fund the smart 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: smartAccount.address,
                network,
                token,
            })
                .catch((err) => {
                console.warn(`⚠️ Failed to request ${token} on ${network} for smart account ${smartAccount.address}: ${err.message}`);
                return null;
            })));
            console.log(`💧 Requested faucet funds for smart account ${smartAccount.address}`);
        }
        catch (error) {
            console.warn(`⚠️ Failed to request faucet funds for smart account ${smartAccount.address}: ${error.message}`);
            // Continue - don't break account creation
        }
    }
    // Return smart account details
    return {
        name: accountName,
        address: smartAccount.address,
        ownerAddress,
        faucet: props.faucet,
    };
});
//# sourceMappingURL=evm-smart-account.js.map