import * as Credential from '../../Credential.js';
import * as Expires from '../../Expires.js';
import * as AcceptPayment from '../../internal/AcceptPayment.js';
import * as core_Mcp from '../../Mcp.js';
/**
 * Creates a payment-aware wrapper around an MCP SDK client.
 *
 * Similar to `Fetch.from()` for HTTP, this wraps an MCP client's `callTool`
 * method to automatically handle payment challenges.
 *
 * @example
 * ```ts
 * import { Client } from '@modelcontextprotocol/sdk/client'
 * import { McpClient, tempo } from 'mppx/mcp-sdk/client'
 * import { privateKeyToAccount } from 'viem/accounts'
 *
 * const client = new Client({ name: 'my-client', version: '1.0.0' })
 * await client.connect(transport)
 *
 * const mcp = McpClient.wrap(client, {
 *   methods: [
 *     tempo({
 *       account: privateKeyToAccount('0x...'),
 *     }),
 *   ],
 * })
 *
 * // Automatically handles payment challenges
 * const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
 * console.log(result.content, result.receipt)
 * ```
 */
export function wrap(client, config) {
    const { methods } = config;
    const paymentPreferences = AcceptPayment.resolve(methods);
    return {
        ...client,
        async callTool(params, options) {
            const context = options?.context;
            const timeout = options?.timeout;
            try {
                const result = await client.callTool(params, undefined, timeout !== undefined ? { timeout } : undefined);
                return {
                    ...result,
                    receipt: result._meta?.[core_Mcp.receiptMetaKey],
                };
            }
            catch (error) {
                // Check if this is a payment required error
                if (!isPaymentRequiredError(error))
                    throw error;
                const challenges = error.data?.challenges;
                if (!challenges?.length)
                    throw error;
                const selected = AcceptPayment.selectChallenge(challenges, methods, paymentPreferences.entries);
                if (!selected) {
                    const available = challenges.map((c) => `${c.method}.${c.intent}`).join(', ');
                    const installed = methods.map((m) => `${m.name}.${m.intent}`).join(', ');
                    throw new Error(`No compatible payment method. Server offers: ${available}. Client has: ${installed}`, { cause: error });
                }
                const credential = await createCredential(selected.challenge, {
                    context,
                    methods,
                });
                const parsed = Credential.deserialize(credential);
                const retryResult = await client.callTool({
                    ...params,
                    _meta: {
                        ...params._meta,
                        [core_Mcp.credentialMetaKey]: parsed,
                    },
                }, undefined, timeout !== undefined ? { timeout } : undefined);
                return {
                    ...retryResult,
                    receipt: retryResult._meta?.[core_Mcp.receiptMetaKey],
                };
            }
        },
    };
}
/**
 * Checks if an error is a payment required error.
 */
export function isPaymentRequiredError(error) {
    if (typeof error !== 'object' || error === null)
        return false;
    if (!('code' in error) || !('message' in error))
        return false;
    if (error.code !== core_Mcp.paymentRequiredCode)
        return false;
    const data = error.data;
    return Array.isArray(data?.challenges) && data.challenges.length > 0;
}
/** @internal */
async function createCredential(challenge, config) {
    const { context, methods } = config;
    const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent);
    if (!mi)
        throw new Error(`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`);
    if (challenge.expires)
        Expires.assert(challenge.expires, challenge.id);
    const parsedContext = mi.context && context !== undefined ? mi.context.parse(context) : undefined;
    return mi.createCredential(parsedContext !== undefined ? { challenge, context: parsedContext } : { challenge });
}
//# sourceMappingURL=McpClient.js.map