/**
 * <folk-payment-page> — Public payment page for QR code payment requests.
 *
 * Three-tab layout:
 * 1. Card — Email → Transak iframe for fiat-to-crypto
 * 2. Wallet — EIP-6963 wallet discovery → ERC-20 transfer
 * 3. EncryptID — Passkey auth → derive EOA → sign tx
 *
 * Polls GET /api/payments/:id for status updates.
 */

class FolkPaymentPage extends HTMLElement {
	private shadow: ShadowRoot;
	private space = 'default';
	private paymentId = '';
	private payment: any = null;
	private loading = true;
	private error = '';
	private activeTab: 'card' | 'wallet' | 'encryptid' = 'card';
	private pollTimer: ReturnType<typeof setInterval> | null = null;

	// Card tab state
	private cardEmail = '';
	private cardLoading = false;
	private transakUrl = '';
	private transakEnv: 'STAGING' | 'PRODUCTION' = 'PRODUCTION';
	private transakPopup: Window | null = null;

	// Wallet tab state
	private walletProviders: any[] = [];
	private walletDiscovery: any = null;
	private walletConnected = false;
	private walletAccount = '';
	private walletSending = false;
	private walletTxHash = '';
	private walletError = '';
	private selectedProviderUuid = '';

	// EncryptID tab state
	private eidSigning = false;
	private eidTxHash = '';
	private eidError = '';

	// Editable amount (for amountEditable payments)
	private customAmount = '';

	// Payer-chosen payment type (when paymentType === 'payer_choice')
	private chosenPaymentType: 'single' | 'subscription' = 'single';

	// QR state
	private qrDataUrl = '';

	constructor() {
		super();
		this.shadow = this.attachShadow({ mode: 'open' });
	}

	connectedCallback() {
		this.space = this.getAttribute('space') || 'default';
		this.paymentId = this.getAttribute('payment-id') || '';
		this.loadPayment();
		this.startPolling();
		this.discoverWallets();
	}

	disconnectedCallback() {
		this.stopPolling();
		this.walletDiscovery?.stop?.();
		window.removeEventListener('message', this.handlePaymentMessage);
	}

	private getApiBase(): string {
		const path = window.location.pathname;
		const match = path.match(/^(\/[^/]+)?\/rcart/);
		return match ? match[0] : '/rcart';
	}

	private async loadPayment() {
		this.loading = true;
		this.render();
		try {
			const res = await fetch(`${this.getApiBase()}/api/payments/${this.paymentId}`);
			if (!res.ok) throw new Error('Payment not found');
			this.payment = await res.json();
			this.generateQR();
		} catch (e) {
			this.error = e instanceof Error ? e.message : 'Failed to load payment';
		}
		this.loading = false;
		this.render();
	}

	private startPolling() {
		this.pollTimer = setInterval(async () => {
			if (!this.paymentId) return;
			try {
				const res = await fetch(`${this.getApiBase()}/api/payments/${this.paymentId}`);
				if (res.ok) {
					const updated = await res.json();
					if (updated.status !== this.payment?.status) {
						this.payment = updated;
						this.render();
					}
				}
			} catch { /* ignore poll errors */ }
		}, 5000);
	}

	private stopPolling() {
		if (this.pollTimer) {
			clearInterval(this.pollTimer);
			this.pollTimer = null;
		}
	}

	private async generateQR() {
		try {
			const QRCode = await import('qrcode');
			const payUrl = `${window.location.origin}${this.getApiBase()}/pay/${this.paymentId}`;
			this.qrDataUrl = await QRCode.toDataURL(payUrl, { margin: 2, width: 200 });
		} catch { /* QR generation optional */ }
	}

	// ── Wallet discovery (EIP-6963) ──

	private async discoverWallets() {
		try {
			const { WalletProviderDiscovery } = await import('../../../src/encryptid/eip6963');
			this.walletDiscovery = new WalletProviderDiscovery();
			this.walletDiscovery.onProvidersChanged((providers: any[]) => {
				this.walletProviders = providers;
				this.render();
			});
			this.walletDiscovery.start();
		} catch { /* no wallet support */ }
	}

	// ── Card tab: Transak ──

	private async startCardPayment() {
		if (!this.cardEmail) return;
		this.cardLoading = true;
		this.render();

		try {
			const effectiveAmount = this.getEffectiveAmount();
			// Use the unified card-session endpoint (picks MoonPay or Transak server-side)
			const res = await fetch(`${this.getApiBase()}/api/payments/${this.paymentId}/card-session`, {
				method: 'POST',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({ email: this.cardEmail, ...(effectiveAmount ? { amount: effectiveAmount } : {}) }),
			});
			const data = await res.json();
			if (!res.ok) throw new Error(data.error || 'Failed to create session');

			this.transakUrl = data.widgetUrl;
			this.transakEnv = data.env || 'sandbox';
			const provider = data.provider || 'transak';

			// Listen for postMessage events from both MoonPay and Transak
			window.addEventListener('message', this.handlePaymentMessage);

			// Open in popup (both providers block iframes)
			this.transakPopup = window.open(
				data.widgetUrl,
				'card-payment',
				'width=450,height=700,scrollbars=yes,resizable=yes',
			);
			// Poll for popup close (user cancelled)
			const popupPoll = setInterval(() => {
				if (this.transakPopup?.closed) {
					clearInterval(popupPoll);
					this.transakPopup = null;
				}
			}, 1000);
		} catch (e) {
			this.error = e instanceof Error ? e.message : String(e);
		}
		this.cardLoading = false;
		this.render();
	}

	private handlePaymentMessage = (e: MessageEvent) => {
		// Transak events
		if (e.data?.event_id === 'TRANSAK_ORDER_SUCCESSFUL' || e.data?.event_id === 'TRANSAK_ORDER_COMPLETED') {
			const orderId = e.data.data?.id || e.data.data?.orderId;
			this.updatePaymentStatus('paid', 'transak', null, orderId);
			window.removeEventListener('message', this.handlePaymentMessage);
		}
		// MoonPay events
		if (e.data?.type === 'moonpay_onTransactionCompleted' || e.data?.type === 'MOONPAY_TRANSACTION_COMPLETED') {
			const txId = e.data.data?.id || e.data.transactionId;
			this.updatePaymentStatus('paid', 'moonpay', null, txId);
			window.removeEventListener('message', this.handlePaymentMessage);
		}
	};

	// ── Wallet tab: EIP-6963 ──

	private async connectWallet(uuid: string) {
		const provider = this.walletDiscovery?.getProvider(uuid);
		if (!provider) return;

		this.selectedProviderUuid = uuid;
		this.walletError = '';

		try {
			const { ExternalSigner } = await import('../../../src/encryptid/external-signer');
			const signer = new ExternalSigner(provider.provider);

			// Switch to correct chain
			await signer.switchChain(this.payment.chainId);

			const accounts = await signer.getAccounts();
			if (accounts.length === 0) throw new Error('No accounts');

			this.walletAccount = accounts[0];
			this.walletConnected = true;
		} catch (e) {
			this.walletError = e instanceof Error ? e.message : String(e);
		}
		this.render();
	}

	private async sendWalletPayment() {
		if (!this.walletConnected || !this.walletAccount) return;

		const provider = this.walletDiscovery?.getProvider(this.selectedProviderUuid);
		if (!provider) return;

		this.walletSending = true;
		this.walletError = '';
		this.render();

		try {
			const { ExternalSigner } = await import('../../../src/encryptid/external-signer');
			const signer = new ExternalSigner(provider.provider);

			const p = this.payment;
			const effectiveAmount = this.getEffectiveAmount();
			if (!effectiveAmount || effectiveAmount === '0') throw new Error('Enter an amount');
			let txHash: string;

			if (p.token === 'ETH') {
				// Native ETH transfer
				const weiAmount = this.parseTokenAmount(effectiveAmount, 18);
				txHash = await signer.sendTransaction({
					from: this.walletAccount,
					to: p.recipientAddress,
					value: '0x' + weiAmount.toString(16),
					chainId: String(p.chainId),
				});
			} else {
				// ERC-20 transfer (USDC: 6 decimals)
				const usdcAddress = p.usdcAddress;
				if (!usdcAddress) throw new Error('USDC not supported on this chain');

				const decimals = p.token === 'USDC' ? 6 : 18;
				const rawAmount = this.parseTokenAmount(effectiveAmount, decimals);

				// transfer(address to, uint256 amount) — selector: 0xa9059cbb
				const recipient = p.recipientAddress.slice(2).toLowerCase().padStart(64, '0');
				const amountHex = rawAmount.toString(16).padStart(64, '0');
				const data = `0xa9059cbb${recipient}${amountHex}`;

				txHash = await signer.sendTransaction({
					from: this.walletAccount,
					to: usdcAddress,
					value: '0x0',
					data,
					chainId: String(p.chainId),
				});
			}

			this.walletTxHash = txHash;
			await this.updatePaymentStatus('paid', 'wallet', txHash);
		} catch (e) {
			this.walletError = e instanceof Error ? e.message : String(e);
		}
		this.walletSending = false;
		this.render();
	}

	// ── EncryptID tab: Passkey-derived EOA ──

	private async payWithEncryptID() {
		this.eidSigning = true;
		this.eidError = '';
		this.render();

		try {
			// 1. Authenticate with passkey + PRF
			const { authenticatePasskey } = await import('../../../src/encryptid/webauthn');
			const { deriveEOAFromPRF } = await import('../../../src/encryptid/eoa-derivation');

			const authResult = await authenticatePasskey();
			if (!authResult.prfOutput) throw new Error('Passkey PRF not supported — try a different browser or device');

			const eoa = deriveEOAFromPRF(new Uint8Array(authResult.prfOutput));

			// 2. Sign transaction with viem
			const { privateKeyToAccount } = await import('viem/accounts');
			const { createWalletClient, http } = await import('viem');
			const { base, baseSepolia, mainnet } = await import('viem/chains');

			const chainMap: Record<number, any> = {
				8453: base,
				84532: baseSepolia,
				1: mainnet,
			};

			const chain = chainMap[this.payment.chainId];
			if (!chain) throw new Error(`Unsupported chain: ${this.payment.chainId}`);

			const hexKey = ('0x' + Array.from(eoa.privateKey).map(b => b.toString(16).padStart(2, '0')).join('')) as `0x${string}`;
			const account = privateKeyToAccount(hexKey);

			const client = createWalletClient({
				account,
				chain,
				transport: http(),
			});

			const p = this.payment;
			const effectiveAmount = this.getEffectiveAmount();
			if (!effectiveAmount || effectiveAmount === '0') throw new Error('Enter an amount');
			let txHash: `0x${string}`;

			if (p.token === 'ETH') {
				txHash = await client.sendTransaction({
					account,
					to: p.recipientAddress as `0x${string}`,
					value: this.parseTokenAmount(effectiveAmount, 18),
					chain,
				});
			} else {
				// ERC-20 transfer
				const usdcAddress = p.usdcAddress;
				if (!usdcAddress) throw new Error('USDC not supported on this chain');

				const decimals = p.token === 'USDC' ? 6 : 18;
				const rawAmount = this.parseTokenAmount(effectiveAmount, decimals);

				const recipient = p.recipientAddress.slice(2).toLowerCase().padStart(64, '0');
				const amountHex = rawAmount.toString(16).padStart(64, '0');
				const data = `0xa9059cbb${recipient}${amountHex}` as `0x${string}`;

				txHash = await client.sendTransaction({
					account,
					to: usdcAddress as `0x${string}`,
					value: 0n,
					data,
					chain,
				});
			}

			// Zero the private key
			eoa.privateKey.fill(0);

			this.eidTxHash = txHash;
			await this.updatePaymentStatus('paid', 'encryptid', txHash);
		} catch (e) {
			this.eidError = e instanceof Error ? e.message : String(e);
		}
		this.eidSigning = false;
		this.render();
	}

	/** Get the effective payment amount — custom amount for editable payments, or the preset amount. */
	private getEffectiveAmount(): string {
		const p = this.payment;
		if (p.amountEditable && this.customAmount) return this.customAmount;
		if (p.amount && p.amount !== '0') return p.amount;
		return this.customAmount || '0';
	}

	/**
	 * Parse a decimal amount string into a BigInt with the given decimals.
	 * Avoids floating-point precision loss for 18-decimal tokens.
	 * e.g. parseTokenAmount("1.5", 18) → 1500000000000000000n
	 */
	private parseTokenAmount(amount: string, decimals: number): bigint {
		const parts = amount.split('.');
		const whole = parts[0] || '0';
		let frac = (parts[1] || '').slice(0, decimals).padEnd(decimals, '0');
		return BigInt(whole) * BigInt(10 ** decimals) + BigInt(frac);
	}

	// ── Status update ──

	private async updatePaymentStatus(status: string, method: string, txHash?: string | null, transakOrderId?: string | null) {
		try {
			const p = this.payment;
			await fetch(`${this.getApiBase()}/api/payments/${this.paymentId}/status`, {
				method: 'PATCH',
				headers: { 'Content-Type': 'application/json' },
				body: JSON.stringify({
					status,
					paymentMethod: method,
					txHash: txHash || undefined,
					transakOrderId: transakOrderId || undefined,
					payerEmail: this.cardEmail || undefined,
					// When payer_choice, tell server what the payer chose
					...(p?.paymentType === 'payer_choice' ? { chosenPaymentType: this.chosenPaymentType } : {}),
				}),
			});
			await this.loadPayment();
		} catch { /* will be picked up by polling */ }
	}

	// ── Render ──

	private render() {
		this.shadow.innerHTML = `
		<style>${this.getStyles()}</style>
		<div class="payment-page">
			${this.loading ? '<div class="loading">Loading payment...</div>' :
				this.error && !this.payment ? `<div class="error">${this.esc(this.error)}</div>` :
				this.payment ? this.renderPayment() : '<div class="error">Payment not found</div>'}
		</div>`;
		this.bindEvents();
	}

	private renderPayment(): string {
		const p = this.payment;
		const isPaid = p.status === 'paid' || p.status === 'confirmed';
		const isExpired = p.status === 'expired';
		const isCancelled = p.status === 'cancelled';
		const isFilled = p.status === 'filled';
		const isTerminal = isPaid || isExpired || isCancelled || isFilled;

		const chainNames: Record<number, string> = { 8453: 'Base', 84532: 'Base Sepolia', 1: 'Ethereum' };
		const chainName = chainNames[p.chainId] || `Chain ${p.chainId}`;
		const isTestnet = p.chainId === 84532;

		const showAmountInput = p.amountEditable && p.status === 'pending';
		const displayAmount = (!p.amount || p.amount === '0') && p.amountEditable ? 'Any amount' : `${p.amount} ${p.token}`;

		// Recipient display: username + truncated wallet
		const recipientName = p.creatorUsername || null;
		const shortWallet = p.recipientAddress
			? p.recipientAddress.slice(0, 6) + '...' + p.recipientAddress.slice(-4)
			: '';

		// Build available methods description
		const methods = p.enabledMethods || { card: true, wallet: true, encryptid: true };
		const methodLabels: string[] = [];
		if (methods.card) methodLabels.push('Credit Card (via Transak)');
		if (methods.wallet) methodLabels.push('External Wallet (MetaMask, etc.)');
		if (methods.encryptid) methodLabels.push('EncryptID Passkey');

		return `
			${isTestnet ? `<div class="staging-banner">
				This is a staging environment for testing credit card to stablecoin and asset-backed CRDT token transactions. Your credit card will not be charged.
			</div>` : ''}

			<div class="header">
				<h1 class="title">Payment Request</h1>
				<div class="status-badge status-${p.status}">${p.status}</div>
			</div>

			<div class="recipient-info">
				You are sending a payment to ${recipientName
					? `<strong>${this.esc(recipientName)}</strong> (<code>${this.esc(shortWallet)}</code>)`
					: `<code>${this.esc(shortWallet)}</code>`}${p.description ? ` for <strong>${this.esc(p.description)}</strong>` : ''}.
				${methodLabels.length > 0 ? `<br/>You can pay by: ${methodLabels.join(', ')}.` : ''}
			</div>

			<div class="amount-display">
				${showAmountInput ? `
					<div class="editable-amount">
						<input type="number" class="amount-input" data-field="custom-amount" step="0.01" min="0.01"
							placeholder="${p.amount && p.amount !== '0' ? p.amount : 'Enter amount'}"
							value="${this.customAmount || ''}" />
						<span class="amount-token">${this.esc(p.token)}</span>
					</div>
					<span class="chain-info">on ${chainName}</span>
				` : `
					<span class="amount">${displayAmount}</span>
					${p.fiatAmount ? `<span class="fiat-amount">\u2248 $${this.esc(p.fiatAmount)} ${this.esc(p.fiatCurrency)}</span>` : ''}
					<span class="chain-info">on ${chainName}</span>
				`}
			</div>

			${p.description ? `<div class="description">${this.esc(p.description)}</div>` : ''}

			${p.paymentType === 'subscription' || (p.paymentType === 'payer_choice' && this.chosenPaymentType === 'subscription') ? `
				<div class="type-badge">Subscription${p.interval ? ` &middot; ${p.interval}` : ''}</div>
				${p.paymentHistory?.length > 0 ? `<div class="payment-history-summary">Payment ${p.paymentCount} of ${p.maxPayments > 0 ? p.maxPayments : '&infin;'}${p.nextDueAt ? ` &middot; Next due: ${new Date(p.nextDueAt).toLocaleDateString()}` : ''}</div>` : ''}
			` : ''}
			${p.paymentType === 'payer_choice' && p.status === 'pending' ? `
				<div class="payer-type-chooser">
					<span class="chooser-label">How would you like to pay?</span>
					<div class="toggle-group">
						<button class="toggle-btn ${this.chosenPaymentType === 'single' ? 'active' : ''}" data-choose-type="single">One-time</button>
						<button class="toggle-btn ${this.chosenPaymentType === 'subscription' ? 'active' : ''}" data-choose-type="subscription">Recurring</button>
					</div>
				</div>` : ''}
			${p.maxPayments > 0 ? `<div class="inventory-bar">
				<div class="inventory-label">${p.paymentCount || 0} / ${p.maxPayments} payments</div>
				<div class="inventory-track"><div class="inventory-fill" style="width:${Math.min(100, ((p.paymentCount || 0) / p.maxPayments) * 100)}%"></div></div>
			</div>` : ''}

			${isPaid ? this.renderPaidConfirmation() :
				isFilled ? '<div class="terminal-msg">This payment request has reached its limit.</div>' :
				isTerminal ? `<div class="terminal-msg">${isExpired ? 'This payment request has expired.' : 'This payment request has been cancelled.'}</div>` :
				this.renderPaymentTabs()}

			<div class="footer">
				${this.qrDataUrl ? `
					<div class="qr-section">
						<img class="qr-code" src="${this.qrDataUrl}" alt="QR Code" />
						<div class="share-url">
							<input type="text" value="${window.location.href}" readonly class="share-input" />
							<button class="btn btn-sm" data-action="copy-url">Copy</button>
						</div>
					</div>` : ''}
			</div>`;
	}

	private renderPaidConfirmation(): string {
		const p = this.payment;
		const explorerBase: Record<number, string> = {
			8453: 'https://basescan.org/tx/',
			84532: 'https://sepolia.basescan.org/tx/',
			1: 'https://etherscan.io/tx/',
		};
		const explorer = explorerBase[p.chainId] || '';

		const cartLink = p.linkedCartId
			? `<div style="margin-top:1.25rem"><a href="${this.getApiBase()}/carts" class="btn btn-primary" style="display:inline-block; text-decoration:none; text-align:center;">Return to Cart</a></div>`
			: '';

		return `
			<div class="confirmation">
				<div class="confirm-icon">&#10003;</div>
				<h2>Payment Complete</h2>
				<div class="confirm-details">
					${p.paymentMethod ? `<div>Method: <strong>${p.paymentMethod}</strong></div>` : ''}
					${p.txHash ? `<div>Transaction: <a href="${explorer}${p.txHash}" target="_blank" rel="noopener">${p.txHash.slice(0, 10)}...${p.txHash.slice(-8)}</a></div>` : ''}
					${p.paid_at ? `<div>Paid: ${new Date(p.paid_at).toLocaleString()}</div>` : ''}
				</div>
				${cartLink}
			</div>`;
	}

	private renderPaymentTabs(): string {
		const methods = this.payment.enabledMethods || { card: true, wallet: true, encryptid: true };
		const enabledTabs: Array<{ id: string; label: string }> = [];
		if (methods.card) enabledTabs.push({ id: 'card', label: 'Card' });
		if (methods.wallet) enabledTabs.push({ id: 'wallet', label: 'Wallet' });
		if (methods.encryptid) enabledTabs.push({ id: 'encryptid', label: 'EncryptID' });

		if (enabledTabs.length === 0) {
			return '<div class="terminal-msg">No payment methods are available for this request.</div>';
		}

		// Auto-select first enabled tab if current tab is disabled
		if (!enabledTabs.find(t => t.id === this.activeTab)) {
			this.activeTab = enabledTabs[0].id as any;
		}

		return `
			<div class="tabs">
				${enabledTabs.map(t =>
					`<button class="tab ${this.activeTab === t.id ? 'active' : ''}" data-tab="${t.id}">${t.label}</button>`
				).join('')}
			</div>

			<div class="tab-content">
				${this.activeTab === 'card' ? this.renderCardTab() :
					this.activeTab === 'wallet' ? this.renderWalletTab() :
					this.renderEncryptIDTab()}
			</div>`;
	}

	private renderCardTab(): string {
		if (this.transakUrl) {
			// Transak blocks iframes (X-Frame-Options: sameorigin) — show popup status
			return `
				<div class="tab-body" style="text-align:center">
					<div class="transak-popup-status">
						<p class="tab-desc">Transak payment opened in a new window.</p>
						<p class="tab-desc" style="font-size:0.8125rem; color:var(--rs-text-muted)">Complete the payment in the popup window. This page will update automatically.</p>
						<button class="btn" data-action="reopen-card-payment" style="margin-top:0.5rem">Re-open Payment Window</button>
					</div>
				</div>`;
		}

		return `
			<div class="tab-body">
				<p class="tab-desc">Pay with credit or debit card via Transak.</p>
				<div class="form-row">
					<input type="email" placeholder="Your email address" class="input" data-field="card-email" value="${this.esc(this.cardEmail)}" />
				</div>
				<button class="btn btn-primary" data-action="start-card-payment" ${this.cardLoading ? 'disabled' : ''}>
					${this.cardLoading ? 'Loading...' : 'Pay with Card'}
				</button>
				${this.error ? `<div class="field-error">${this.esc(this.error)}</div>` : ''}
			</div>`;
	}

	private renderWalletTab(): string {
		if (this.walletTxHash) {
			return `
				<div class="tab-body">
					<div class="success-msg">Transaction sent!</div>
					<div class="tx-hash">Tx: ${this.walletTxHash.slice(0, 14)}...${this.walletTxHash.slice(-10)}</div>
				</div>`;
		}

		if (this.walletConnected) {
			return `
				<div class="tab-body">
					<p class="tab-desc">Connected: ${this.walletAccount.slice(0, 6)}...${this.walletAccount.slice(-4)}</p>
					<button class="btn btn-primary" data-action="send-wallet" ${this.walletSending ? 'disabled' : ''}>
						${this.walletSending ? 'Sending...' : `Send ${this.getEffectiveAmount() || '?'} ${this.payment.token}`}
					</button>
					${this.walletError ? `<div class="field-error">${this.esc(this.walletError)}</div>` : ''}
				</div>`;
		}

		if (this.walletProviders.length === 0) {
			return `
				<div class="tab-body">
					<p class="tab-desc">No wallets detected. Install MetaMask or another EIP-6963 compatible wallet.</p>
				</div>`;
		}

		return `
			<div class="tab-body">
				<p class="tab-desc">Select a wallet to connect:</p>
				<div class="wallet-list">
					${this.walletProviders.map((p: any) => `
						<button class="wallet-btn" data-wallet-uuid="${p.info.uuid}">
							<img src="${p.info.icon}" alt="" class="wallet-icon" />
							<span>${this.esc(p.info.name)}</span>
						</button>
					`).join('')}
				</div>
				${this.walletError ? `<div class="field-error">${this.esc(this.walletError)}</div>` : ''}
			</div>`;
	}

	private renderEncryptIDTab(): string {
		if (this.eidTxHash) {
			return `
				<div class="tab-body">
					<div class="success-msg">Transaction sent!</div>
					<div class="tx-hash">Tx: ${this.eidTxHash.slice(0, 14)}...${this.eidTxHash.slice(-10)}</div>
				</div>`;
		}

		return `
			<div class="tab-body">
				<p class="tab-desc">Pay using your EncryptID passkey. Your signing key is derived locally from your passkey and never leaves your device.</p>
				<button class="btn btn-primary" data-action="pay-encryptid" ${this.eidSigning ? 'disabled' : ''}>
					${this.eidSigning ? 'Signing...' : 'Pay with Passkey'}
				</button>
				${this.eidError ? `<div class="field-error">${this.esc(this.eidError)}</div>` : ''}
			</div>`;
	}

	private bindEvents() {
		// Tab switching
		this.shadow.querySelectorAll('[data-tab]').forEach((el) => {
			el.addEventListener('click', () => {
				this.activeTab = (el as HTMLElement).dataset.tab as any;
				this.render();
			});
		});

		// Custom amount for editable payments
		const customAmtInput = this.shadow.querySelector('[data-field="custom-amount"]') as HTMLInputElement;
		customAmtInput?.addEventListener('input', () => { this.customAmount = customAmtInput.value; });

		// Payer payment type chooser
		this.shadow.querySelectorAll('[data-choose-type]').forEach((el) => {
			el.addEventListener('click', () => {
				this.chosenPaymentType = (el as HTMLElement).dataset.chooseType as 'single' | 'subscription';
				this.render();
			});
		});

		// Card tab
		const emailInput = this.shadow.querySelector('[data-field="card-email"]') as HTMLInputElement;
		emailInput?.addEventListener('input', () => { this.cardEmail = emailInput.value; });
		this.shadow.querySelector('[data-action="start-card-payment"]')?.addEventListener('click', () => this.startCardPayment());
		this.shadow.querySelector('[data-action="reopen-card-payment"]')?.addEventListener('click', () => {
			if (this.transakUrl) {
				this.transakPopup = window.open(
					this.transakUrl,
					'transak-payment',
					'width=450,height=700,scrollbars=yes,resizable=yes',
				);
			}
		});

		// Wallet tab
		this.shadow.querySelectorAll('[data-wallet-uuid]').forEach((el) => {
			el.addEventListener('click', () => this.connectWallet((el as HTMLElement).dataset.walletUuid!));
		});
		this.shadow.querySelector('[data-action="send-wallet"]')?.addEventListener('click', () => this.sendWalletPayment());

		// EncryptID tab
		this.shadow.querySelector('[data-action="pay-encryptid"]')?.addEventListener('click', () => this.payWithEncryptID());

		// Copy URL
		this.shadow.querySelector('[data-action="copy-url"]')?.addEventListener('click', () => {
			navigator.clipboard.writeText(window.location.href);
			const btn = this.shadow.querySelector('[data-action="copy-url"]') as HTMLElement;
			if (btn) { btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy'; }, 2000); }
		});
	}

	private getStyles(): string {
		return `
			:host { display: block; padding: 1.5rem; width: 100%; max-width: 560px; margin: 0 auto; -webkit-tap-highlight-color: transparent; overflow-wrap: break-word; word-wrap: break-word; }
			* { box-sizing: border-box; }
			button, a, input, select, textarea, [role="button"] { touch-action: manipulation; }

			.payment-page { }

			.staging-banner { background: rgba(251,191,36,0.12); border: 1px solid rgba(251,191,36,0.3); color: #fbbf24; border-radius: 8px; padding: 0.75rem 1rem; font-size: 0.8125rem; line-height: 1.5; margin-bottom: 1rem; text-align: center; }
			.recipient-info { color: var(--rs-text-secondary); font-size: 0.875rem; line-height: 1.6; margin-bottom: 1.25rem; padding: 0.75rem 1rem; background: var(--rs-bg-surface); border-radius: 8px; border: 1px solid var(--rs-border); }
			.recipient-info strong { color: var(--rs-text-primary); }
			.recipient-info code { font-size: 0.8125rem; background: rgba(255,255,255,0.06); padding: 0.125rem 0.375rem; border-radius: 4px; word-break: break-all; }

			.header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; }
			.title { color: var(--rs-text-primary); font-size: 1.25rem; font-weight: 700; margin: 0; }

			.status-badge { padding: 0.25rem 0.75rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; }
			.status-pending { background: rgba(251,191,36,0.15); color: #fbbf24; }
			.status-paid, .status-confirmed { background: rgba(34,197,94,0.15); color: #4ade80; }
			.status-expired { background: rgba(239,68,68,0.15); color: #f87171; }
			.status-cancelled { background: rgba(156,163,175,0.15); color: #9ca3af; }
			.status-filled { background: rgba(99,102,241,0.15); color: #818cf8; }

			.type-badge { text-align: center; margin-bottom: 0.75rem; }
			.type-badge { display: inline-block; padding: 0.1875rem 0.625rem; border-radius: 999px; font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; background: rgba(99,102,241,0.12); color: #818cf8; }
			.payment-history-summary { text-align: center; color: var(--rs-text-muted); font-size: 0.8125rem; margin-bottom: 0.75rem; }

			.payer-type-chooser { text-align: center; margin-bottom: 1rem; padding: 0.75rem; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 8px; }
			.chooser-label { display: block; font-size: 0.8125rem; color: var(--rs-text-secondary); margin-bottom: 0.5rem; }
			.toggle-group { display: inline-flex; gap: 0; border: 1px solid var(--rs-border); border-radius: 8px; overflow: hidden; }
			.toggle-btn { padding: 0.4375rem 1rem; border: none; background: var(--rs-bg-surface); color: var(--rs-text-secondary); cursor: pointer; font-size: 0.8125rem; font-weight: 500; transition: all 0.15s; }
			.toggle-btn:not(:last-child) { border-right: 1px solid var(--rs-border); }
			.toggle-btn.active { background: var(--rs-primary-hover); color: #fff; }
			.toggle-btn:hover:not(.active) { background: var(--rs-bg-hover); }

			.inventory-bar { margin-bottom: 1rem; padding: 0.5rem 0.75rem; background: var(--rs-bg-surface); border: 1px solid var(--rs-border); border-radius: 8px; }
			.inventory-label { font-size: 0.75rem; color: var(--rs-text-secondary); margin-bottom: 0.375rem; text-align: center; }
			.inventory-track { height: 6px; border-radius: 3px; background: rgba(255,255,255,0.08); overflow: hidden; }
			.inventory-fill { height: 100%; border-radius: 3px; background: var(--rs-primary-hover); transition: width 0.3s ease; }

			.amount-display { text-align: center; margin-bottom: 1rem; }
			.amount { display: block; font-size: 2rem; font-weight: 700; color: var(--rs-text-primary); }
			.fiat-amount { display: block; font-size: 0.875rem; color: var(--rs-text-secondary); margin-top: 0.25rem; }
			.chain-info { display: block; font-size: 0.75rem; color: var(--rs-text-muted); margin-top: 0.25rem; }

			.editable-amount { display: flex; align-items: center; justify-content: center; gap: 0.5rem; }
			.amount-input { width: 160px; padding: 0.5rem 0.75rem; border-radius: 8px; border: 1px solid var(--rs-input-border); background: var(--rs-input-bg); color: var(--rs-text-primary); font-size: 1.75rem; font-weight: 700; text-align: center; }
			.amount-input:focus { outline: none; border-color: var(--rs-primary); }
			.amount-input::placeholder { color: var(--rs-text-muted); font-weight: 400; font-size: 1rem; }
			.amount-token { color: var(--rs-text-secondary); font-size: 1.25rem; font-weight: 600; }

			.description { text-align: center; color: var(--rs-text-secondary); font-size: 0.9375rem; margin-bottom: 1.5rem; padding: 0.75rem; background: var(--rs-bg-surface); border-radius: 8px; border: 1px solid var(--rs-border); }

			.tabs { display: flex; gap: 0; border-bottom: 1px solid var(--rs-border); margin-bottom: 1.5rem; }
			.tab { flex: 1; padding: 0.75rem 1rem; border: none; background: none; color: var(--rs-text-secondary); cursor: pointer; font-size: 0.875rem; font-weight: 500; border-bottom: 2px solid transparent; transition: all 0.15s; }
			.tab:hover { color: var(--rs-text-primary); }
			.tab.active { color: var(--rs-primary-hover); border-bottom-color: var(--rs-primary); }

			.tab-content { min-height: 180px; }
			.tab-body { }
			.tab-desc { color: var(--rs-text-secondary); font-size: 0.875rem; margin: 0 0 1rem; line-height: 1.5; }

			.form-row { margin-bottom: 1rem; }

			.input { width: 100%; padding: 0.625rem 0.75rem; border-radius: 8px; border: 1px solid var(--rs-input-border); background: var(--rs-input-bg); color: var(--rs-input-text); font-size: 0.875rem; }
			.input:focus { outline: none; border-color: var(--rs-primary); }

			.btn { padding: 0.625rem 1.25rem; border-radius: 8px; border: 1px solid var(--rs-border); background: var(--rs-bg-surface); color: var(--rs-text-primary); cursor: pointer; font-size: 0.875rem; font-weight: 500; }
			.btn:hover { border-color: var(--rs-border-strong); }
			.btn-primary { background: var(--rs-primary-hover); border-color: var(--rs-primary); color: #fff; width: 100%; }
			.btn-primary:hover { background: #4338ca; }
			.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
			.btn-sm { padding: 0.375rem 0.75rem; font-size: 0.8125rem; }

			.wallet-list { display: flex; flex-direction: column; gap: 0.5rem; }
			.wallet-btn { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1rem; border-radius: 8px; border: 1px solid var(--rs-border); background: var(--rs-bg-surface); color: var(--rs-text-primary); cursor: pointer; font-size: 0.875rem; transition: border-color 0.15s; }
			.wallet-btn:hover { border-color: var(--rs-primary); }
			.wallet-icon { width: 28px; height: 28px; border-radius: 6px; }

			.field-error { color: #f87171; font-size: 0.8125rem; margin-top: 0.75rem; }
			.success-msg { color: #4ade80; font-size: 1rem; font-weight: 600; text-align: center; margin-bottom: 0.5rem; }
			.tx-hash { color: var(--rs-text-secondary); font-size: 0.8125rem; text-align: center; font-family: monospace; word-break: break-all; }

			.confirmation { text-align: center; padding: 2rem 1rem; }
			.confirm-icon { font-size: 3rem; color: #4ade80; margin-bottom: 0.5rem; }
			.confirmation h2 { color: var(--rs-text-primary); font-size: 1.25rem; margin: 0 0 1rem; }
			.confirm-details { color: var(--rs-text-secondary); font-size: 0.875rem; line-height: 1.8; }
			.confirm-details a { color: var(--rs-primary-hover); text-decoration: none; }
			.confirm-details a:hover { text-decoration: underline; }

			.terminal-msg { text-align: center; padding: 2rem; color: var(--rs-text-muted); font-size: 0.875rem; }

			.transak-container { border-radius: 8px; overflow: hidden; border: 1px solid var(--rs-border); }
			.transak-iframe { width: 100%; height: clamp(400px, 72vh, 600px); border: none; }

			.footer { margin-top: 2rem; border-top: 1px solid var(--rs-border); padding-top: 1.5rem; }
			.qr-section { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
			.qr-code { border-radius: 8px; background: #fff; padding: 8px; }
			.share-url { display: flex; gap: 0.5rem; width: 100%; }
			.share-input { flex: 1; min-width: 0; padding: 0.5rem 0.75rem; border-radius: 8px; border: 1px solid var(--rs-input-border); background: var(--rs-input-bg); color: var(--rs-input-text); font-size: 0.75rem; font-family: monospace; overflow: hidden; text-overflow: ellipsis; }

			.loading { text-align: center; padding: 3rem; color: var(--rs-text-secondary); }
			.error { text-align: center; padding: 3rem; color: #f87171; }

			@media (max-width: 600px) {
				:host { padding: 1rem 0.75rem; }
				.staging-banner { font-size: 0.75rem; padding: 0.625rem 0.75rem; }
				.recipient-info { font-size: 0.8125rem; padding: 0.625rem 0.75rem; }
				.header { flex-direction: column; align-items: flex-start; gap: 0.5rem; margin-bottom: 1rem; }
				.title { font-size: 1.125rem; }
				.amount { font-size: 1.5rem; }
				.amount-input { font-size: 1.25rem; width: 120px; }
				.amount-token { font-size: 1rem; }
				.description { font-size: 0.875rem; padding: 0.625rem; }
				.tab { padding: 0.625rem 0.5rem; font-size: 0.8125rem; }
				.tab-content { min-height: 140px; }
				.transak-iframe { height: min(500px, 70vh); }
				.confirmation { padding: 1.5rem 0.5rem; }
				.confirm-details { font-size: 0.8125rem; }
				.share-url { flex-direction: column; }
				.share-input { font-size: 0.6875rem; }
				.toggle-group { flex-wrap: wrap; }
				.toggle-btn { padding: 0.375rem 0.625rem; font-size: 0.75rem; }
				.footer { margin-top: 1.5rem; padding-top: 1rem; }
				.payer-type-chooser { padding: 0.5rem; }
			}
			@media (max-width: 380px) {
				:host { padding: 0.75rem 0.5rem; }
				.staging-banner { font-size: 0.6875rem; padding: 0.5rem; }
				.recipient-info { font-size: 0.75rem; padding: 0.5rem; }
				.recipient-info code { font-size: 0.6875rem; }
				.title { font-size: 1rem; }
				.amount { font-size: 1.25rem; }
				.amount-input { font-size: 1rem; width: 100px; padding: 0.375rem 0.5rem; }
				.tab { font-size: 0.75rem; padding: 0.5rem 0.375rem; }
				.tab-desc { font-size: 0.8125rem; }
				.btn { font-size: 0.8125rem; padding: 0.5rem 0.75rem; }
				.confirm-icon { font-size: 2rem; }
				.qr-code { width: 150px; }
			}
		`;
	}

	private esc(s: string): string {
		const d = document.createElement('div');
		d.textContent = s;
		return d.innerHTML;
	}
}

customElements.define('folk-payment-page', FolkPaymentPage);
