<?php


$isConnected = $isConnected ?? !empty($marketplaceConfigured);
?>

<?php if (empty($skipMpBanner)): ?>

<div class="bg-white rounded-xl border border-gray-200 px-4 py-2.5 mb-4 flex items-center justify-between">
    <div class="flex items-center gap-3">
        <div id="mpDot" class="w-2 h-2 rounded-full <?= $isConnected ? 'bg-green-500' : 'bg-gray-300' ?>"></div>
        <span id="mpLabel" class="text-xs <?= $isConnected ? 'text-green-600' : 'text-gray-500' ?>"><?= $isConnected ? 'Connected' : 'Not connected' ?></span>
        <span id="mpBalanceBadge" class="<?= $isConnected ? '' : 'hidden' ?> inline-flex items-center gap-1 px-2 py-0.5 bg-green-50 border border-green-200 rounded-full text-xs font-semibold text-green-700">
            <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818l.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
            <span id="mpBalanceAmount">...</span>
        </span>
        <button type="button" id="mpTopUpBtn" onclick="MP.openTopUpModal()" class="<?= $isConnected ? '' : 'hidden' ?> inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-semibold text-primary-700 bg-primary-50 hover:bg-primary-100 border border-primary-200 rounded-lg transition-colors">
            <svg class="w-3 h-3" fill="none" stroke="currentColor" stroke-width="2.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/></svg>
            Top Up
        </button>
    </div>
    <div class="flex items-center gap-2">
        <button type="button" id="mpConnectBtn" onclick="MP.autoConnect()" class="<?= $isConnected ? 'hidden' : '' ?> inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-white bg-primary-600 hover:bg-primary-700 rounded-lg transition-colors">
            <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m9.86-2.54a4.5 4.5 0 00-1.242-7.244l-4.5-4.5a4.5 4.5 0 00-6.364 6.364L4.343 8.28"/></svg>
            Connect
        </button>
        <button type="button" id="mpDisconnectBtn" onclick="MP.disconnect()" class="<?= $isConnected ? '' : 'hidden' ?> inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold text-red-600 bg-red-50 hover:bg-red-100 border border-red-200 rounded-lg transition-colors">
            <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
            Disconnect
        </button>
    </div>
</div>
<?php endif; ?>


<div id="mpTopUpOverlay" class="hidden fixed inset-0 bg-black/40 z-[60] transition-opacity" onclick="MP.closeTopUpModal()"></div>
<div id="mpTopUpModal" class="hidden fixed inset-0 z-[60] flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col" style="max-height:min(92vh, 720px)" onclick="event.stopPropagation()">
        <div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between shrink-0">
            <div>
                <h3 class="text-base font-bold text-gray-900" id="mpTopUpTitle">Top Up Credits</h3>
                <p class="text-xs text-gray-500 mt-0.5">Current balance: <strong id="mpTopUpCurrentBalance" class="text-green-600">0</strong></p>
            </div>
            <button type="button" onclick="MP.closeTopUpModal()" class="p-1.5 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
            </button>
        </div>
        <div class="px-6 py-5 overflow-y-auto flex-1 min-h-0">
            
            <div id="mpTopUpStep1">
                <div id="mpTopUpPacks" class="grid grid-cols-2 gap-3">
                    <div class="col-span-2 text-center py-8 text-gray-400 text-sm">
                        <svg class="w-6 h-6 mx-auto mb-2 animate-spin text-gray-300" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/></svg>
                        Loading packs...
                    </div>
                </div>
            </div>

            
            <div id="mpTopUpStep2" class="hidden">
                <p class="text-sm text-gray-600 mb-4">Please fill in your billing information before proceeding to payment.</p>
                <form id="mpBillingForm" class="space-y-3">
                    
                    <div class="flex rounded-lg overflow-hidden border border-gray-300">
                        <button type="button" onclick="MP.setBillingType('individual')" data-btype="individual"
                            class="mp-billing-toggle flex-1 py-2 text-xs font-semibold text-center transition-colors bg-primary-600 text-white">
                            <i class="fa-solid fa-user mr-1"></i> Individual
                        </button>
                        <button type="button" onclick="MP.setBillingType('business')" data-btype="business"
                            class="mp-billing-toggle flex-1 py-2 text-xs font-semibold text-center transition-colors bg-gray-100 text-gray-600">
                            <i class="fa-solid fa-building mr-1"></i> Business
                        </button>
                    </div>
                    <input type="hidden" name="billing_type" value="individual">

                    
                    <div id="mpBusinessFields" class="hidden space-y-3">
                        <label class="block">
                            <span class="text-xs font-medium text-gray-700">Company Name <span class="text-red-500">*</span></span>
                            <input type="text" name="company" placeholder="Acme Corp." class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
                        </label>
                        <label class="block">
                            <span class="text-xs font-medium text-gray-700">VAT Number <span class="text-gray-400 font-normal">(optional)</span></span>
                            <input type="text" name="vat_number" placeholder="EU123456789" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
                        </label>
                    </div>

                    
                    <div class="flex gap-3">
                        <label class="flex-1">
                            <span class="text-xs font-medium text-gray-700">First Name <span class="text-red-500">*</span></span>
                            <input type="text" name="first_name" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                        </label>
                        <label class="flex-1">
                            <span class="text-xs font-medium text-gray-700">Last Name <span class="text-red-500">*</span></span>
                            <input type="text" name="last_name" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                        </label>
                    </div>

                    
                    <label class="block">
                        <span class="text-xs font-medium text-gray-700">Email <span class="text-red-500">*</span></span>
                        <input type="email" name="email" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                    </label>

                    
                    <label class="block">
                        <span class="text-xs font-medium text-gray-700">Country <span class="text-red-500">*</span></span>
                        <select name="country" id="mpBillingCountry" onchange="MP.toggleStateField()" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white" required>
                            <option value="">Select country...</option>
                            <option value="US">United States</option>
                            <option value="GB">United Kingdom</option>
                            <option value="CA">Canada</option>
                            <option value="FR">France</option>
                            <option value="DE">Germany</option>
                            <option value="ES">Spain</option>
                            <option value="IT">Italy</option>
                            <option value="NL">Netherlands</option>
                            <option value="BE">Belgium</option>
                            <option value="CH">Switzerland</option>
                            <option value="AT">Austria</option>
                            <option value="PT">Portugal</option>
                            <option value="IE">Ireland</option>
                            <option value="SE">Sweden</option>
                            <option value="NO">Norway</option>
                            <option value="DK">Denmark</option>
                            <option value="FI">Finland</option>
                            <option value="PL">Poland</option>
                            <option value="CZ">Czech Republic</option>
                            <option value="RO">Romania</option>
                            <option value="AU">Australia</option>
                            <option value="NZ">New Zealand</option>
                            <option value="JP">Japan</option>
                            <option value="SG">Singapore</option>
                            <option value="HK">Hong Kong</option>
                            <option value="BR">Brazil</option>
                            <option value="MX">Mexico</option>
                            <option value="IN">India</option>
                            <option value="ZA">South Africa</option>
                        </select>
                    </label>

                    
                    <label class="block">
                        <span class="text-xs font-medium text-gray-700">Address <span class="text-red-500">*</span></span>
                        <input type="text" name="address_line1" placeholder="Street address" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                    </label>
                    <label class="block">
                        <input type="text" name="address_line2" placeholder="Apartment, suite, unit, etc. (optional)" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
                    </label>

                    
                    <div class="flex gap-3">
                        <label class="flex-1">
                            <span class="text-xs font-medium text-gray-700">City <span class="text-red-500">*</span></span>
                            <input type="text" name="city" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                        </label>
                        <label class="w-28 hidden" id="mpStateGroup">
                            <span class="text-xs font-medium text-gray-700">State</span>
                            <input type="text" name="state" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
                        </label>
                        <label class="w-28">
                            <span class="text-xs font-medium text-gray-700">Postal Code <span class="text-red-500">*</span></span>
                            <input type="text" name="postal_code" class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-primary-500" required>
                        </label>
                    </div>

                    <button type="submit" class="w-full mt-2 py-2.5 bg-primary-600 text-white text-sm font-semibold rounded-lg hover:bg-primary-700 transition-colors">
                        Continue to Payment
                    </button>
                </form>
            </div>

            
            <div id="mpTopUpStep3" class="hidden">
                <div id="mpStripeCheckout" class="min-h-[300px]"></div>
            </div>

            
            <div id="mpTopUpStep4" class="hidden text-center py-6">
                <div class="w-16 h-16 mx-auto mb-4 bg-green-100 rounded-full flex items-center justify-center">
                    <svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
                </div>
                <h4 class="text-lg font-bold text-gray-900 mb-1">Payment Successful!</h4>
                <p class="text-sm text-gray-600">Your new balance: <strong id="mpNewBalance" class="text-green-600">0</strong></p>
            </div>
        </div>
        <div id="mpTopUpFooter" class="px-6 py-3 bg-gray-50 border-t border-gray-100 flex items-center justify-between">
            <span class="text-[11px] text-gray-400">
                <svg class="w-3 h-3 inline -mt-0.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z"/></svg>
                Secure payment via Stripe
            </span>
            <button type="button" onclick="MP.closeTopUpModal()" class="text-xs text-gray-500 hover:text-gray-700">Cancel</button>
        </div>
    </div>
</div>


<div id="mpPurchaseOverlay" class="hidden fixed inset-0 bg-black/40 z-[60] transition-opacity" onclick="MP.closePurchaseModal()"></div>
<div id="mpPurchaseModal" class="hidden fixed inset-0 z-[60] flex items-center justify-center p-4">
    <div class="bg-white rounded-2xl shadow-2xl w-full max-w-md overflow-hidden" onclick="event.stopPropagation()">
        <div class="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
            <div>
                <h3 id="mpPurchaseName" class="text-base font-bold text-gray-900">Purchase Item</h3>
                <p class="text-xs text-gray-500 mt-0.5">Your balance: <strong id="mpPurchaseBalance" class="text-green-600">0</strong></p>
            </div>
            <button type="button" onclick="MP.closePurchaseModal()" class="p-1.5 text-gray-400 hover:text-gray-600 rounded-lg hover:bg-gray-100 transition-colors">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
            </button>
        </div>

        
        <div id="mpPurchaseLoading" class="px-6 py-8 text-center text-gray-400 text-sm">
            <svg class="w-6 h-6 mx-auto mb-2 animate-spin text-gray-300" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/></svg>
            Loading...
        </div>

        
        <div id="mpPurchaseContent" class="hidden px-6 py-5">
            <div id="mpTierCards" class="space-y-3 mb-4"></div>

            <div id="mpInsufficientCredits" class="hidden mb-4 p-3 bg-amber-50 border border-amber-200 rounded-lg">
                <p class="text-sm text-amber-800 font-medium">Insufficient Credits</p>
                <p class="text-xs text-amber-600 mt-1">You need more Credits for this purchase.</p>
                <button type="button" onclick="MP.topUpThenRetry()" class="mt-2 text-xs font-semibold text-primary-700 hover:text-primary-800 underline">
                    Top Up Credits
                </button>
            </div>

            <button type="button" id="mpPurchaseBtn" onclick="MP.confirmPurchase()"
                    class="w-full py-2.5 bg-primary-600 text-white text-sm font-semibold rounded-lg hover:bg-primary-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2">
                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
                <span id="mpPurchaseBtnText">Purchase & Install</span>
            </button>
        </div>

        
        <div id="mpPurchaseInstalling" class="hidden px-6 py-8 text-center">
            <svg class="w-8 h-8 mx-auto mb-3 animate-spin text-primary-500" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/></svg>
            <p class="text-sm font-medium text-gray-700">Purchasing & Installing...</p>
            <p class="text-xs text-gray-400 mt-1">This may take a moment</p>
        </div>

        
        <div id="mpPurchaseSuccess" class="hidden px-6 py-8 text-center">
            <div class="w-16 h-16 mx-auto mb-4 bg-green-100 rounded-full flex items-center justify-center">
                <svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
            </div>
            <h4 class="text-lg font-bold text-gray-900 mb-1">Installed!</h4>
            <p id="mpPurchaseSuccessMsg" class="text-sm text-gray-600"></p>
            <button id="mpPurchaseSuccessBtn" type="button" onclick="window.location.reload()" class="mt-4 px-6 py-2 bg-primary-600 text-white text-sm font-semibold rounded-lg hover:bg-primary-700">
                Reload Page
            </button>
        </div>

        
        <div id="mpPurchaseError" class="hidden px-6 py-6 text-center">
            <div class="w-12 h-12 mx-auto mb-3 bg-red-100 rounded-full flex items-center justify-center">
                <svg class="w-6 h-6 text-red-600" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
            </div>
            <p id="mpPurchaseErrorMsg" class="text-sm text-red-700 font-medium"></p>
            <button type="button" onclick="MP.closePurchaseModal()" class="mt-3 text-xs text-gray-500 hover:text-gray-700">Close</button>
        </div>
    </div>
</div>


<script>
window.MP = (function() {
    const CSRF = '<?= Router::csrfToken() ?>';
    const CREDIT_RATE = <?= (float)($creditRate ?? 0.01) ?>;
    let connected = <?= $isConnected ? 'true' : 'false' ?>;
    let balance = null;
    let cachedPacks = null;
    let stripeInstance = null;
    let embeddedCheckout = null;
    let currentPurchase = { slug: '', type: '', name: '', selectedTierId: null, selectedTierPrice: 0 };
    let topUpResolve = null; // For top-up-then-retry flow

    function init() {
        if (connected) {
            fetchBalance();
        }
    }

    // ── Connection state ──

    function setConnected(state) {
        connected = state;
        const dot = document.getElementById('mpDot');
        const label = document.getElementById('mpLabel');
        const connectBtn = document.getElementById('mpConnectBtn');
        const disconnectBtn = document.getElementById('mpDisconnectBtn');
        const topUpBtn = document.getElementById('mpTopUpBtn');
        const badge = document.getElementById('mpBalanceBadge');
        if (dot) dot.className = 'w-2 h-2 rounded-full ' + (state ? 'bg-green-500' : 'bg-gray-300');
        if (label) { label.textContent = state ? 'Connected' : 'Not connected'; label.className = 'text-xs ' + (state ? 'text-green-600' : 'text-gray-500'); }
        if (connectBtn) connectBtn.classList.toggle('hidden', state);
        if (disconnectBtn) disconnectBtn.classList.toggle('hidden', !state);
        if (topUpBtn) topUpBtn.classList.toggle('hidden', !state);
        if (!state && badge) badge.classList.add('hidden');
    }

    function autoConnect() {
        const w = 480, h = 600;
        const left = (screen.width - w) / 2;
        const top = (screen.height - h) / 2;
        const popup = window.open(
            'https://tubepress.io/connect?origin=' + encodeURIComponent(window.location.origin),
            'tubepress_connect',
            'width=' + w + ',height=' + h + ',left=' + left + ',top=' + top + ',resizable=no,scrollbars=no'
        );
        if (!popup) {
            alert('Please allow popups for this site.');
        }
    }

    window.addEventListener('message', async function(e) {
        if (!e.data) return;

        // Handle connection errors (e.g. URL binding limit)
        if (e.data.type === 'tubepress-connect-error') {
            showConnectError(e.data.error || 'Connection failed.');
            return;
        }

        if (e.data.type !== 'tubepress-connect') return;
        // On the import page the CAT module handles this message
        // without reloading (to preserve CSV import state).
        // AT_CONNECTING: same pattern for the AI translate modal.
        if (window.IMP_TABS || window.AT_CONNECTING) return;
        const { api_key, api_secret, credits } = e.data;
        try {
            await fetch('/admin/catalogue/save-config', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                body: JSON.stringify({
                    catalogue_api_key: api_key,
                    catalogue_api_secret: api_secret,
                    catalogue_api_url: 'https://tubepress.io/v1',
                })
            });
        } catch (err) { /* silent */ }
        if (credits !== undefined) updateBalance(credits);
        setConnected(true);
        window.location.reload();
    });

    function showConnectError(msg) {
        // Remove any existing toast
        const existing = document.getElementById('mpConnectErrorToast');
        if (existing) existing.remove();

        const toast = document.createElement('div');
        toast.id = 'mpConnectErrorToast';
        toast.style.cssText = 'position:fixed;top:1rem;right:1rem;z-index:9999;max-width:420px;padding:0.85rem 1.1rem;background:#fef2f2;border:1px solid #fecaca;border-radius:0.75rem;box-shadow:0 4px 20px rgba(0,0,0,0.12);display:flex;align-items:flex-start;gap:0.6rem;animation:slideIn .3s ease;';
        toast.innerHTML = '<div style="flex-shrink:0;width:22px;height:22px;background:#fee2e2;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-top:1px;"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#dc2626" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg></div>'
            + '<div style="flex:1;min-width:0;"><div style="font-size:0.8rem;font-weight:700;color:#991b1b;margin-bottom:2px;">Connection Error</div><div style="font-size:0.78rem;color:#b91c1c;line-height:1.4;">' + msg.replace(/</g, '&lt;') + '</div></div>'
            + '<button onclick="this.parentElement.remove()" style="flex-shrink:0;background:none;border:none;color:#9ca3af;cursor:pointer;font-size:1.1rem;line-height:1;padding:0;">&times;</button>';
        document.body.appendChild(toast);
        setTimeout(() => { if (toast.parentElement) toast.style.opacity = '0'; toast.style.transition = 'opacity .3s'; setTimeout(() => toast.remove(), 300); }, 10000);
    }

    async function disconnect() {
        if (!confirm('Disconnect from TubePress? Your API credentials will be removed.')) return;
        try {
            await fetch('/admin/catalogue/save-config', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                body: JSON.stringify({ catalogue_api_key: '', catalogue_api_secret: '', catalogue_api_url: '' })
            });
        } catch (e) { /* silent */ }
        setConnected(false);
        window.location.reload();
    }

    // ── Balance ──

    async function fetchBalance() {
        try {
            const resp = await fetch('/admin/catalogue/balance');
            const data = await resp.json();
            if (data.credits !== undefined) updateBalance(data.credits);
        } catch (e) { /* silent */ }
    }

    function updateBalance(credits) {
        balance = parseFloat(credits);
        const usd = (balance * CREDIT_RATE).toFixed(2);
        const amountEl = document.getElementById('mpBalanceAmount');
        const badgeEl = document.getElementById('mpBalanceBadge');
        if (amountEl) amountEl.textContent = balance.toFixed(0) + ' Credits (~$' + usd + ')';
        if (badgeEl) badgeEl.classList.remove('hidden');
    }

    // ── Top Up Modal (multi-step) ──

    function showTopUpStep(step) {
        for (let i = 1; i <= 4; i++) {
            document.getElementById('mpTopUpStep' + i).classList.toggle('hidden', i !== step);
        }
        const titles = { 1: 'Top Up Credits', 2: 'Billing Information', 3: 'Payment', 4: 'Top Up Credits' };
        document.getElementById('mpTopUpTitle').textContent = titles[step] || 'Top Up Credits';
        document.getElementById('mpTopUpFooter').classList.toggle('hidden', step === 3);
    }

    async function openTopUpModal() {
        document.getElementById('mpTopUpOverlay').classList.remove('hidden');
        document.getElementById('mpTopUpModal').classList.remove('hidden');
        document.body.style.overflow = 'hidden';

        const balanceEl = document.getElementById('mpBalanceAmount');
        if (balanceEl && balanceEl.textContent && balanceEl.textContent !== '...') {
            document.getElementById('mpTopUpCurrentBalance').textContent = balanceEl.textContent;
        } else if (balance !== null) {
            const usd = (balance * CREDIT_RATE).toFixed(2);
            document.getElementById('mpTopUpCurrentBalance').textContent = balance.toFixed(0) + ' Credits (~$' + usd + ')';
        } else {
            document.getElementById('mpTopUpCurrentBalance').textContent = '0';
        }

        showTopUpStep(1);

        if (!cachedPacks) {
            try {
                const resp = await fetch('/admin/catalogue/packs');
                const data = await resp.json();
                cachedPacks = data.packs || [];
            } catch (e) {
                cachedPacks = [];
            }
        }
        renderTopUpPacks(cachedPacks);
    }

    function closeTopUpModal() {
        document.getElementById('mpTopUpOverlay').classList.add('hidden');
        document.getElementById('mpTopUpModal').classList.add('hidden');
        document.body.style.overflow = '';
        destroyCheckout();
        if (topUpResolve) {
            topUpResolve(false);
            topUpResolve = null;
        }
    }

    function renderTopUpPacks(packs) {
        const container = document.getElementById('mpTopUpPacks');
        if (!packs.length) {
            container.innerHTML = '<div class="col-span-2 text-center py-8 text-gray-400 text-sm">No credit packs available.</div>';
            return;
        }
        const bestIdx = packs.length - 1;
        container.innerHTML = packs.map((p, i) => {
            const isBest = i === bestIdx;
            const credits = Number(p.credits).toLocaleString();
            const price = p.price_display || ('$' + (p.price_cents / 100).toFixed(2));
            const perCredit = p.per_credit || ('$' + (p.price_cents / p.credits / 100).toFixed(4));
            return `<button type="button" onclick="MP.buyPack(${p.id})"
                class="relative flex flex-col items-center gap-1.5 p-4 rounded-xl border-2 transition-all duration-200 hover:shadow-md hover:scale-[1.02] cursor-pointer group ${isBest ? 'border-primary-500 bg-primary-50/50 shadow-sm' : 'border-gray-200 hover:border-primary-300 bg-white'}">
                ${isBest ? '<span class="absolute -top-2.5 left-1/2 -translate-x-1/2 px-2 py-0.5 bg-primary-600 text-white text-[9px] font-bold uppercase tracking-wider rounded-full">Best Value</span>' : ''}
                <span class="text-lg font-extrabold text-gray-900">${credits}</span>
                <span class="text-[10px] font-medium text-gray-400 uppercase tracking-wide">Credits</span>
                <span class="text-xl font-extrabold ${isBest ? 'text-primary-600' : 'text-gray-800'}">${price}</span>
                <span class="text-[10px] text-gray-400">${perCredit}/credit</span>
                <span class="mt-1 w-full py-1.5 rounded-lg text-xs font-semibold text-center transition-colors ${isBest ? 'bg-primary-600 text-white group-hover:bg-primary-700' : 'bg-gray-100 text-gray-700 group-hover:bg-primary-600 group-hover:text-white'}">
                    Buy Now
                </span>
            </button>`;
        }).join('');
    }

    async function buyPack(packId) {
        // Step 1 → check billing → step 2 or step 3
        try {
            const resp = await fetch('/admin/catalogue/billing-status');
            const data = await resp.json();

            if (data.has_billing) {
                // Skip billing form, go straight to Stripe
                await startCheckout(packId);
            } else {
                // Show billing form, pre-fill if data available
                showTopUpStep(2);
                if (data.billing) {
                    const form = document.getElementById('mpBillingForm');
                    for (const [k, v] of Object.entries(data.billing)) {
                        if (!v) continue;
                        const input = form.querySelector('[name="' + k + '"]');
                        if (input) input.value = v;
                    }
                    // Sync billing type toggle
                    if (data.billing.billing_type) setBillingType(data.billing.billing_type);
                    // Sync country select & state visibility
                    toggleStateField();
                }
                // Attach submit handler
                document.getElementById('mpBillingForm').onsubmit = async function(e) {
                    e.preventDefault();
                    const formData = Object.fromEntries(new FormData(this));
                    const btn = this.querySelector('button[type="submit"]');
                    btn.disabled = true;
                    btn.textContent = 'Saving...';
                    try {
                        await fetch('/admin/catalogue/save-billing', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                            body: JSON.stringify(formData)
                        });
                        await startCheckout(packId);
                    } catch (err) {
                        btn.disabled = false;
                        btn.textContent = 'Continue to Payment';
                        alert('Failed to save billing info. Please try again.');
                    }
                };
            }
        } catch (err) {
            // Fallback: go straight to checkout
            await startCheckout(packId);
        }
    }

    async function startCheckout(packId) {
        showTopUpStep(3);
        try {
            const resp = await fetch('/admin/catalogue/create-checkout', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                body: JSON.stringify({ pack_id: packId })
            });
            const data = await resp.json();
            if (data.error) {
                alert(data.error);
                showTopUpStep(1);
                return;
            }
            await mountCheckout(data.client_secret, data.publishable_key, data.session_id);
        } catch (err) {
            alert('Failed to create checkout session.');
            showTopUpStep(1);
        }
    }

    // ── Stripe Embedded Checkout ──

    async function loadStripe() {
        if (window.Stripe) return window.Stripe;
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = 'https://js.stripe.com/v3/';
            script.onload = () => resolve(window.Stripe);
            script.onerror = () => reject(new Error('Failed to load Stripe.js'));
            document.head.appendChild(script);
        });
    }

    async function mountCheckout(clientSecret, publishableKey, sessionId) {
        try {
            const StripeFactory = await loadStripe();
            if (!stripeInstance || stripeInstance._publishableKey !== publishableKey) {
                stripeInstance = StripeFactory(publishableKey);
                stripeInstance._publishableKey = publishableKey;
            }

            embeddedCheckout = await stripeInstance.initEmbeddedCheckout({
                clientSecret: clientSecret,
                onComplete: () => onCheckoutComplete(sessionId),
            });

            embeddedCheckout.mount('#mpStripeCheckout');
        } catch (err) {
            console.error('Stripe mount error:', err);
            alert('Failed to load payment form. Please try again.');
            showTopUpStep(1);
        }
    }

    function destroyCheckout() {
        if (embeddedCheckout) {
            try { embeddedCheckout.destroy(); } catch (e) { /* silent */ }
            embeddedCheckout = null;
        }
        document.getElementById('mpStripeCheckout').innerHTML = '';
    }

    async function onCheckoutComplete(sessionId) {
        destroyCheckout();

        // Poll session status until balance is updated
        let attempts = 0;
        const poll = async () => {
            try {
                const resp = await fetch('/admin/catalogue/session-status?session_id=' + encodeURIComponent(sessionId));
                const data = await resp.json();
                if (data.status === 'complete' && data.balance !== undefined) {
                    updateBalance(data.balance);
                    const bal = parseFloat(data.balance);
                    const balUsd = (bal * CREDIT_RATE).toFixed(2);
                    const balText = bal.toFixed(0) + ' Credits (~$' + balUsd + ')';
                    document.getElementById('mpNewBalance').textContent = balText;
                    document.getElementById('mpTopUpCurrentBalance').textContent = balText;
                    showTopUpStep(4);
                    if (topUpResolve) {
                        topUpResolve(true);
                        topUpResolve = null;
                    }
                    return;
                }
            } catch (e) { /* silent */ }
            attempts++;
            if (attempts < 15) setTimeout(poll, 2000);
            else {
                // Fallback: refresh balance
                await fetchBalance();
                showTopUpStep(4);
                document.getElementById('mpNewBalance').textContent = balance !== null ? balance.toFixed(2) : '?';
                if (topUpResolve) {
                    topUpResolve(true);
                    topUpResolve = null;
                }
            }
        };
        setTimeout(poll, 1500);
    }

    // ── Purchase Modal (tier selection) ──

    async function openPurchaseModal(slug, type, name) {
        if (!connected) {
            autoConnect();
            return;
        }

        currentPurchase = { slug, type, name, selectedTierId: null, selectedTierPrice: 0 };

        // Reset UI
        document.getElementById('mpPurchaseName').textContent = name;
        document.getElementById('mpPurchaseLoading').classList.remove('hidden');
        document.getElementById('mpPurchaseContent').classList.add('hidden');
        document.getElementById('mpPurchaseInstalling').classList.add('hidden');
        document.getElementById('mpPurchaseSuccess').classList.add('hidden');
        document.getElementById('mpPurchaseError').classList.add('hidden');

        document.getElementById('mpPurchaseOverlay').classList.remove('hidden');
        document.getElementById('mpPurchaseModal').classList.remove('hidden');
        document.body.style.overflow = 'hidden';

        try {
            const resp = await fetch('/admin/catalogue/marketplace-item-info?type=' + encodeURIComponent(type) + '&slug=' + encodeURIComponent(slug));
            const data = await resp.json();

            if (data.error) {
                showPurchaseError(data.error);
                return;
            }

            const item = data.item;
            balance = parseFloat(data.balance) || 0;
            updateBalance(balance);
            document.getElementById('mpPurchaseBalance').textContent = balance.toFixed(0) + ' Credits (~$' + (balance * CREDIT_RATE).toFixed(2) + ')';

            // Already owned? Just install
            if (item.owned) {
                await doInstall(slug, type);
                return;
            }

            // Render tier cards
            const tiers = item.pricing_tiers || [];
            const tiersContainer = document.getElementById('mpTierCards');

            if (tiers.length > 0) {
                const basePrice = tiers.length > 0 ? parseFloat(tiers[0].price_credits) : 0;
                tiersContainer.innerHTML = tiers.map((tier, i) => {
                    const price = parseFloat(tier.price_credits);
                    const sites = tier.max_sites == 0 ? 'Unlimited' : tier.max_sites;
                    const label = tier.label || (sites + ' Site' + (tier.max_sites != 1 ? 's' : ''));
                    const perSite = tier.max_sites > 0 ? (price / tier.max_sites).toFixed(0) : '';
                    const savings = i > 0 && basePrice > 0 && tier.max_sites > 0
                        ? Math.round((1 - (price / tier.max_sites) / (basePrice / (tiers[0].max_sites || 1))) * 100)
                        : 0;
                    const usd = (price * CREDIT_RATE).toFixed(2);
                    return `<label class="flex items-center gap-3 p-3 rounded-xl border-2 cursor-pointer transition-all hover:border-primary-300 ${i === 0 ? 'border-primary-500 bg-primary-50/30' : 'border-gray-200'}" data-tier-card>
                        <input type="radio" name="mp_tier" value="${tier.id}" data-price="${price}" ${i === 0 ? 'checked' : ''}
                            class="w-4 h-4 text-primary-600 border-gray-300 focus:ring-primary-500"
                            onchange="MP.selectTier(${tier.id}, ${price})">
                        <div class="flex-1">
                            <div class="flex items-center gap-2">
                                <span class="text-sm font-semibold text-gray-900">${label}</span>
                                ${savings > 0 ? '<span class="px-1.5 py-0.5 bg-green-100 text-green-700 text-[10px] font-bold rounded">Save ' + savings + '%</span>' : ''}
                            </div>
                            ${perSite ? '<p class="text-[11px] text-gray-400 mt-0.5">' + Number(perSite).toLocaleString() + ' Credits/site</p>' : ''}
                        </div>
                        <div class="text-right">
                            <span class="text-sm font-bold text-gray-900">${Number(price).toLocaleString()} Credits</span>
                            <p class="text-[11px] text-gray-400">~$${usd}</p>
                        </div>
                    </label>`;
                }).join('');

                // Default to first tier
                selectTier(tiers[0].id, parseFloat(tiers[0].price_credits));
            } else {
                // No tiers — single price
                const price = parseFloat(item.price_credits);
                const singleUsd = (price * CREDIT_RATE).toFixed(2);
                tiersContainer.innerHTML = `<div class="p-3 rounded-xl border-2 border-primary-500 bg-primary-50/30">
                    <div class="flex items-center justify-between">
                        <span class="text-sm font-semibold text-gray-900">${name}</span>
                        <div class="text-right">
                            <span class="text-sm font-bold text-gray-900">${Number(price).toLocaleString()} Credits</span>
                            <p class="text-[11px] text-gray-400">~$${singleUsd}</p>
                        </div>
                    </div>
                </div>`;
                currentPurchase.selectedTierId = null;
                currentPurchase.selectedTierPrice = price;
                updatePurchaseButton();
            }

            document.getElementById('mpPurchaseLoading').classList.add('hidden');
            document.getElementById('mpPurchaseContent').classList.remove('hidden');

        } catch (err) {
            showPurchaseError('Failed to load item details.');
        }
    }

    function selectTier(tierId, price) {
        currentPurchase.selectedTierId = tierId;
        currentPurchase.selectedTierPrice = price;

        // Update card visual
        document.querySelectorAll('[data-tier-card]').forEach(card => {
            const radio = card.querySelector('input[type="radio"]');
            const isSelected = radio && parseInt(radio.value) === tierId;
            card.className = card.className.replace(/border-primary-500 bg-primary-50\/30|border-gray-200/g, '');
            card.classList.add(isSelected ? 'border-primary-500' : 'border-gray-200');
            if (isSelected) card.classList.add('bg-primary-50/30');
        });

        updatePurchaseButton();
    }

    function updatePurchaseButton() {
        const btn = document.getElementById('mpPurchaseBtn');
        const btnText = document.getElementById('mpPurchaseBtnText');
        const insufficient = document.getElementById('mpInsufficientCredits');
        const price = currentPurchase.selectedTierPrice;

        const usdPrice = (price * CREDIT_RATE).toFixed(2);
        btnText.textContent = 'Purchase & Install (' + Number(price).toLocaleString() + ' Credits \u2248 $' + usdPrice + ')';

        if (balance !== null && balance < price) {
            insufficient.classList.remove('hidden');
            btn.disabled = true;
        } else {
            insufficient.classList.add('hidden');
            btn.disabled = false;
        }
    }

    function closePurchaseModal() {
        document.getElementById('mpPurchaseOverlay').classList.add('hidden');
        document.getElementById('mpPurchaseModal').classList.add('hidden');
        document.body.style.overflow = '';
    }

    function showPurchaseError(msg) {
        document.getElementById('mpPurchaseLoading').classList.add('hidden');
        document.getElementById('mpPurchaseContent').classList.add('hidden');
        document.getElementById('mpPurchaseInstalling').classList.add('hidden');
        document.getElementById('mpPurchaseError').classList.remove('hidden');
        document.getElementById('mpPurchaseErrorMsg').textContent = msg;
    }

    async function confirmPurchase() {
        const { slug, type, selectedTierId, selectedTierPrice } = currentPurchase;

        // Show installing state
        document.getElementById('mpPurchaseContent').classList.add('hidden');
        document.getElementById('mpPurchaseInstalling').classList.remove('hidden');

        try {
            // 1. Purchase via marketplace
            const purchaseResp = await fetch('/admin/catalogue/marketplace-purchase', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': CSRF },
                body: JSON.stringify({ item_type: type, item_slug: slug, tier_id: selectedTierId })
            });
            const purchaseData = await purchaseResp.json();

            if (purchaseData.error) {
                if (purchaseResp.status === 402) {
                    // Insufficient credits — offer top up
                    document.getElementById('mpPurchaseInstalling').classList.add('hidden');
                    document.getElementById('mpPurchaseContent').classList.remove('hidden');
                    updatePurchaseButton();
                } else {
                    showPurchaseError(purchaseData.error);
                }
                return;
            }

            // Update balance
            if (purchaseData.new_balance !== undefined) {
                updateBalance(purchaseData.new_balance);
                const nb = parseFloat(purchaseData.new_balance);
                document.getElementById('mpPurchaseBalance').textContent = nb.toFixed(0) + ' Credits (~$' + (nb * CREDIT_RATE).toFixed(2) + ')';
            }

            // 2. Install
            await doInstall(slug, type);

        } catch (err) {
            showPurchaseError('Purchase failed. Please try again.');
        }
    }

    async function doInstall(slug, type) {
        document.getElementById('mpPurchaseLoading').classList.add('hidden');
        document.getElementById('mpPurchaseContent').classList.add('hidden');
        document.getElementById('mpPurchaseInstalling').classList.remove('hidden');

        try {
            const installUrl = type === 'plugin'
                ? '/admin/plugins/' + encodeURIComponent(slug) + '/install'
                : '/admin/themes/' + encodeURIComponent(slug) + '/install';

            const formData = new FormData();
            formData.append('csrf_token', CSRF);

            const resp = await fetch(installUrl, {
                method: 'POST',
                headers: { 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' },
                body: formData,
            });
            const data = await resp.json();

            if (data.success) {
                document.getElementById('mpPurchaseInstalling').classList.add('hidden');
                document.getElementById('mpPurchaseSuccess').classList.remove('hidden');
                document.getElementById('mpPurchaseSuccessMsg').textContent = data.message || 'Successfully installed!';

                // Change button to "Activate Plugin/Theme" with form POST
                const btn = document.getElementById('mpPurchaseSuccessBtn');
                const label = type === 'plugin' ? 'Activate Plugin' : 'Activate Theme';
                btn.textContent = label;
                btn.onclick = function() {
                    const form = document.createElement('form');
                    form.method = 'POST';
                    form.action = '/admin/' + (type === 'plugin' ? 'plugins' : 'themes') + '/' + encodeURIComponent(slug) + '/activate';
                    const csrf = document.createElement('input');
                    csrf.type = 'hidden';
                    csrf.name = 'csrf_token';
                    csrf.value = CSRF;
                    form.appendChild(csrf);
                    document.body.appendChild(form);
                    form.submit();
                };
            } else {
                showPurchaseError(data.message || 'Installation failed.');
            }
        } catch (err) {
            showPurchaseError('Installation failed. Please try again.');
        }
    }

    // ── Billing form helpers ──

    function setBillingType(type) {
        const form = document.getElementById('mpBillingForm');
        form.querySelector('[name="billing_type"]').value = type;
        form.querySelectorAll('.mp-billing-toggle').forEach(btn => {
            const isActive = btn.dataset.btype === type;
            btn.className = 'mp-billing-toggle flex-1 py-2 text-xs font-semibold text-center transition-colors ' +
                (isActive ? 'bg-primary-600 text-white' : 'bg-gray-100 text-gray-600');
        });
        document.getElementById('mpBusinessFields').classList.toggle('hidden', type !== 'business');
    }

    function toggleStateField() {
        const country = document.getElementById('mpBillingCountry').value;
        const needsState = ['US', 'CA', 'AU', 'IN', 'BR', 'MX'];
        document.getElementById('mpStateGroup').classList.toggle('hidden', !needsState.includes(country));
    }

    // Top up, then retry purchase
    function topUpThenRetry() {
        closePurchaseModal();
        openTopUpModal();
        // When top-up completes, re-open purchase modal
        topUpResolve = function(success) {
            if (success) {
                setTimeout(() => {
                    closeTopUpModal();
                    openPurchaseModal(currentPurchase.slug, currentPurchase.type, currentPurchase.name);
                }, 1500);
            }
        };
    }

    document.addEventListener('DOMContentLoaded', init);

    return {
        autoConnect, disconnect, updateBalance,
        openTopUpModal, closeTopUpModal, buyPack,
        openPurchaseModal, closePurchaseModal, selectTier, confirmPurchase,
        topUpThenRetry, setBillingType, toggleStateField,
        showConnectError,
    };
})();
</script>
