import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
//#region extensions/crypto/src/services/fiat-service.ts
/**
* Fiat Rails Service — off-ramps, on-ramps, and payment routing.
*
* Provides a unified interface for moving money between crypto and fiat.
* Supports multiple providers (Bridge.xyz, MoonPay, Coinbase) with a
* preference-based fallback system.
*
* Architecture:
* - Provider-agnostic interface: all providers implement FiatProvider
* - Quote aggregation: fetch quotes from multiple providers, pick best
* - Idempotent transfers: each transfer gets a unique ID for tracking
* - Persistent state: transfer history saved to disk for accounting
*
* Env vars:
*   BRIDGE_API_KEY         — Bridge.xyz API key
*   MOONPAY_API_KEY        — MoonPay API key (optional)
*   COINBASE_API_KEY       — Coinbase Pay API key (optional)
*   FIAT_CURRENCY          — Default fiat currency (default: USD)
*/
var BridgeProvider = class {
	name = "bridge";
	isConfigured() {
		return !!process.env.BRIDGE_API_KEY;
	}
	async apiFetch(path, opts) {
		const apiKey = process.env.BRIDGE_API_KEY;
		if (!apiKey) throw new Error("BRIDGE_API_KEY not set");
		const res = await fetch(`https://api.bridge.xyz/v0${path}`, {
			...opts,
			headers: {
				"Content-Type": "application/json",
				"Api-Key": apiKey,
				...opts?.headers ?? {}
			},
			signal: AbortSignal.timeout(3e4)
		});
		if (!res.ok) {
			const body = await res.text().catch(() => "");
			throw new Error(`Bridge API ${res.status}: ${body.slice(0, 200)}`);
		}
		return res.json();
	}
	async getQuote(params) {
		const chain = {
			1: "ethereum",
			8453: "base",
			42161: "arbitrum",
			10: "optimism",
			137: "polygon",
			43114: "avalanche"
		}[params.cryptoChainId] ?? "base";
		const body = {
			source_currency: params.direction === "off_ramp" ? params.cryptoToken.toLowerCase() : params.fiatCurrency.toLowerCase(),
			destination_currency: params.direction === "off_ramp" ? params.fiatCurrency.toLowerCase() : params.cryptoToken.toLowerCase(),
			chain
		};
		if (params.amountType === "crypto") body.amount = String(params.amount);
		else body.destination_amount = String(params.amount);
		const data = await this.apiFetch("/transfers/quote", {
			method: "POST",
			body: JSON.stringify(body)
		});
		const cryptoAmt = parseFloat(data.source_amount ?? data.amount ?? params.amount);
		const fiatAmt = parseFloat(data.destination_amount ?? data.fiat_amount ?? params.amount);
		const fee = parseFloat(data.fee ?? "0");
		return {
			provider: "bridge",
			direction: params.direction,
			cryptoAmount: params.direction === "off_ramp" ? cryptoAmt : fiatAmt,
			cryptoToken: params.cryptoToken,
			cryptoChainId: params.cryptoChainId,
			fiatAmount: params.direction === "off_ramp" ? fiatAmt : cryptoAmt,
			fiatCurrency: params.fiatCurrency,
			fee,
			exchangeRate: fiatAmt / (cryptoAmt || 1),
			expiresIn: 6e4,
			quoteId: data.id ?? `bridge_${Date.now()}`,
			estimatedSettlement: data.estimated_settlement ?? "1-3 business days"
		};
	}
	async executeTransfer(quote, opts) {
		const data = await this.apiFetch("/transfers", {
			method: "POST",
			body: JSON.stringify({
				quote_id: quote.quoteId,
				source_address: opts.walletAddress,
				external_account_id: opts.bankAccountId
			})
		});
		return {
			transferId: data.id ?? `bridge_tx_${Date.now()}`,
			depositAddress: data.deposit_address,
			instructions: data.instructions ?? `Transfer ${quote.cryptoAmount} ${quote.cryptoToken} to the deposit address.`
		};
	}
	async getTransferStatus(transferId) {
		return {
			pending: "pending",
			processing: "processing",
			completed: "completed",
			failed: "failed",
			cancelled: "cancelled"
		}[(await this.apiFetch(`/transfers/${transferId}`)).status] ?? "pending";
	}
	async listBankAccounts() {
		try {
			return ((await this.apiFetch("/external_accounts")).data ?? []).map((a) => ({
				id: a.id,
				label: a.account_name ?? a.label ?? "Bank Account",
				maskedNumber: a.last_4 ? `****${a.last_4}` : "****",
				bankName: a.bank_name ?? "Unknown",
				currency: (a.currency ?? "USD").toUpperCase(),
				externalId: a.id,
				provider: "bridge"
			}));
		} catch {
			return [];
		}
	}
};
var MoonPayProvider = class {
	name = "moonpay";
	isConfigured() {
		return !!process.env.MOONPAY_API_KEY;
	}
	async getQuote(params) {
		const apiKey = process.env.MOONPAY_API_KEY;
		if (!apiKey) throw new Error("MOONPAY_API_KEY not set");
		const endpoint = params.direction === "on_ramp" ? "https://api.moonpay.com/v3/currencies/quote" : "https://api.moonpay.com/v3/sell_quotes";
		const queryParams = new URLSearchParams({
			apiKey,
			baseCurrencyCode: params.fiatCurrency.toLowerCase(),
			currencyCode: params.cryptoToken.toLowerCase(),
			baseCurrencyAmount: String(params.amountType === "fiat" ? params.amount : ""),
			quoteCurrencyAmount: String(params.amountType === "crypto" ? params.amount : "")
		});
		const res = await fetch(`${endpoint}?${queryParams}`, { signal: AbortSignal.timeout(15e3) });
		if (!res.ok) throw new Error(`MoonPay ${res.status}`);
		const data = await res.json();
		return {
			provider: "moonpay",
			direction: params.direction,
			cryptoAmount: parseFloat(data.quoteCurrencyAmount ?? params.amount),
			cryptoToken: params.cryptoToken,
			cryptoChainId: params.cryptoChainId,
			fiatAmount: parseFloat(data.baseCurrencyAmount ?? params.amount),
			fiatCurrency: params.fiatCurrency,
			fee: parseFloat(data.feeAmount ?? data.totalFee ?? "0"),
			exchangeRate: parseFloat(data.quoteCurrencyPrice ?? "0"),
			expiresIn: 3e4,
			quoteId: `moonpay_${Date.now()}`,
			estimatedSettlement: "Instant to 1 business day"
		};
	}
	async executeTransfer(_quote, _opts) {
		const widgetUrl = `https://buy.moonpay.com?apiKey=${process.env.MOONPAY_API_KEY}&currencyCode=${_quote.cryptoToken.toLowerCase()}&walletAddress=${_opts.walletAddress}`;
		return {
			transferId: `moonpay_${Date.now()}`,
			instructions: `Complete the purchase at: ${widgetUrl}`
		};
	}
	async getTransferStatus(_transferId) {
		return "pending";
	}
};
var FiatService = class {
	providers = [];
	transfers = /* @__PURE__ */ new Map();
	stateDir;
	constructor(opts) {
		this.stateDir = opts?.stateDir ?? join(process.env.HOME ?? "", ".openclawnch", "fiat");
		this.providers.push(new BridgeProvider());
		this.providers.push(new MoonPayProvider());
		this.loadState();
	}
	/** Get configured providers. */
	getConfiguredProviders() {
		return this.providers.filter((p) => p.isConfigured()).map((p) => p.name);
	}
	/** Check if any fiat provider is configured. */
	isAvailable() {
		return this.providers.some((p) => p.isConfigured());
	}
	/** Get quotes from all configured providers. */
	async getQuotes(params) {
		const configured = this.providers.filter((p) => p.isConfigured());
		if (configured.length === 0) throw new Error("No fiat providers configured. Set BRIDGE_API_KEY or MOONPAY_API_KEY.");
		const results = await Promise.allSettled(configured.map((p) => p.getQuote({
			direction: params.direction,
			cryptoToken: params.cryptoToken,
			cryptoChainId: params.cryptoChainId ?? 8453,
			amount: params.amount,
			amountType: params.amountType ?? "crypto",
			fiatCurrency: params.fiatCurrency ?? "USD"
		})));
		const quotes = [];
		for (const r of results) if (r.status === "fulfilled") quotes.push(r.value);
		quotes.sort((a, b) => {
			if (params.direction === "off_ramp") return b.fiatAmount - b.fee - (a.fiatAmount - a.fee);
			return b.cryptoAmount - a.cryptoAmount;
		});
		return quotes;
	}
	/** Execute a fiat transfer using a quote. */
	async executeTransfer(quote, opts) {
		const provider = this.providers.find((p) => p.name === quote.provider);
		if (!provider) throw new Error(`Provider "${quote.provider}" not found`);
		const result = await provider.executeTransfer(quote, {
			walletAddress: opts.walletAddress,
			bankAccountId: opts.bankAccountId
		});
		const transfer = {
			id: `fiat_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
			userId: opts.userId,
			direction: quote.direction,
			provider: quote.provider,
			cryptoAmount: quote.cryptoAmount,
			cryptoToken: quote.cryptoToken,
			cryptoChainId: quote.cryptoChainId,
			fiatAmount: quote.fiatAmount,
			fiatCurrency: quote.fiatCurrency,
			fee: quote.fee,
			status: "pending",
			externalId: result.transferId,
			createdAt: Date.now(),
			updatedAt: Date.now()
		};
		this.transfers.set(transfer.id, transfer);
		this.saveState();
		return transfer;
	}
	/** Check transfer status from provider. */
	async refreshTransferStatus(transferId) {
		const transfer = this.transfers.get(transferId);
		if (!transfer || !transfer.externalId) return transfer ?? null;
		const provider = this.providers.find((p) => p.name === transfer.provider);
		if (!provider) return transfer;
		try {
			transfer.status = await provider.getTransferStatus(transfer.externalId);
			transfer.updatedAt = Date.now();
			this.saveState();
		} catch {}
		return transfer;
	}
	/** Get transfer by ID. */
	getTransfer(transferId) {
		return this.transfers.get(transferId) ?? null;
	}
	/** List all transfers for a user. */
	listTransfers(userId) {
		const all = Array.from(this.transfers.values());
		if (!userId) return all;
		return all.filter((t) => t.userId === userId);
	}
	/** List linked bank accounts from configured providers. */
	async listBankAccounts() {
		const accounts = [];
		for (const provider of this.providers) if (provider.isConfigured() && provider.listBankAccounts) try {
			const providerAccounts = await provider.listBankAccounts();
			accounts.push(...providerAccounts);
		} catch {}
		return accounts;
	}
	loadState() {
		try {
			const filePath = join(this.stateDir, "transfers.json");
			if (existsSync(filePath)) {
				const data = JSON.parse(readFileSync(filePath, "utf8"));
				for (const t of data) this.transfers.set(t.id, t);
			}
		} catch {}
	}
	saveState() {
		try {
			if (!existsSync(this.stateDir)) mkdirSync(this.stateDir, { recursive: true });
			writeFileSync(join(this.stateDir, "transfers.json"), JSON.stringify(Array.from(this.transfers.values()), null, 2), "utf8");
		} catch {}
	}
	/** Clear all state (for testing). */
	clear() {
		this.transfers.clear();
	}
};
let instance = null;
function getFiatService(opts) {
	if (!instance) instance = new FiatService(opts);
	return instance;
}
function resetFiatService() {
	instance?.clear();
	instance = null;
}
//#endregion
export { FiatService, getFiatService, resetFiatService };

//# sourceMappingURL=fiat-service.mjs.map