<template>
  <div class="pb-6">

    <!-- Quick Actions Container -->
    <div class="glass-card p-6 mb-6">
      <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-6 gap-4 stagger-grid">
        <!-- Networking Profits -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 0">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <span class="text-2xl text-orange-500 font-bold">₿</span>
            </div>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">{{ t('web5.networkingProfits') }}</p>
              <p class="text-xs text-orange-500 font-medium">{{ networkingProfitsDisplay }}</p>
            </div>
          </div>
          <div v-if="profitsBreakdown" class="text-xs text-white/40 space-y-0.5">
            <p v-if="profitsBreakdown.content_sales_sats > 0">Content: {{ profitsBreakdown.content_sales_sats.toLocaleString() }} sats</p>
            <p v-if="profitsBreakdown.routing_fees_sats > 0">Routing: {{ profitsBreakdown.routing_fees_sats.toLocaleString() }} sats</p>
          </div>
        </div>

        <!-- DID Status -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 1">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <div class="w-3 h-3 rounded-full" :class="didStatus === 'active' ? 'bg-green-400' : 'bg-yellow-400'"></div>
              <div v-if="didStatus === 'active'" class="absolute inset-0 w-3 h-3 rounded-full bg-green-400 animate-ping opacity-75"></div>
            </div>
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium text-white">{{ t('web5.didStatus') }}</p>
              <p v-if="userDid" class="text-xs text-white/60 font-mono truncate" :title="userDid">{{ userDid }}</p>
              <p v-else class="text-xs text-white/60 capitalize">{{ didStatus }}</p>
            </div>
          </div>
          <div v-if="userDid" class="flex gap-2">
            <button
              @click="copyDid"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ didCopied ? t('common.copiedBang') : t('web5.copyDid') }}
            </button>
            <button
              @click="showDidDocument"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ t('web5.viewDidDocument') }}
            </button>
          </div>
          <button
            v-else
            @click="createDID"
            :disabled="creatingDid"
            class="w-fit px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
          >
            {{ creatingDid ? t('web5.creatingDid') : t('web5.createDid') }}
          </button>
        </div>

        <!-- did:dht Status -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 1.5">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <div class="w-3 h-3 rounded-full" :class="dhtDid ? 'bg-blue-400' : 'bg-gray-500'"></div>
            </div>
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium text-white">DHT Identity</p>
              <p v-if="dhtDid" class="text-xs text-white/60 font-mono truncate" :title="dhtDid">{{ dhtDid }}</p>
              <p v-else class="text-xs text-white/60">Not published</p>
            </div>
          </div>
          <div v-if="dhtDid" class="flex gap-2">
            <button
              @click="copyDhtDid"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ dhtDidCopied ? 'Copied!' : 'Copy' }}
            </button>
            <button
              @click="refreshDhtDid"
              :disabled="publishingDht"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
            >
              {{ publishingDht ? 'Refreshing...' : 'Refresh DHT' }}
            </button>
          </div>
          <button
            v-else-if="userDid"
            @click="publishDhtDid"
            :disabled="publishingDht"
            class="w-fit px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
          >
            {{ publishingDht ? 'Publishing...' : 'Publish to DHT' }}
          </button>
        </div>

        <!-- Wallet Connection -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 2">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <div class="w-3 h-3 rounded-full" :class="walletConnected ? 'bg-green-400' : 'bg-red-400'"></div>
              <div v-if="walletConnected" class="absolute inset-0 w-3 h-3 rounded-full bg-green-400 animate-ping opacity-75"></div>
            </div>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">{{ t('web5.wallet') }}</p>
              <p class="text-xs text-white/60">{{ walletConnected ? t('common.connected') : t('common.disconnected') }}</p>
            </div>
          </div>
          <button
            @click="connectWallet"
            class="w-fit px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
            :disabled="connectingWallet"
          >
            {{ connectingWallet ? t('common.connecting') : walletConnected ? t('common.disconnect') : t('common.connect') }}
          </button>
        </div>

        <!-- Nostr Relay Status -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 3">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <div class="w-3 h-3 rounded-full" :class="(nostrRelayStats?.connected_count ?? 0) > 0 ? 'bg-green-400' : 'bg-red-400'"></div>
              <div v-if="(nostrRelayStats?.connected_count ?? 0) > 0" class="absolute inset-0 w-3 h-3 rounded-full bg-green-400 animate-ping opacity-75"></div>
            </div>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">{{ t('web5.nostrRelays') }}</p>
              <p class="text-xs text-white/60">{{ t('web5.relaysConnected', { count: nostrRelayStats?.connected_count ?? 0 }) }}</p>
            </div>
          </div>
          <button
            @click="manageRelays"
            class="w-fit px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
          >
            {{ t('common.manage') }}
          </button>
        </div>

        <!-- Connected Nodes -->
        <div data-controller-container tabindex="0" class="card-stagger flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0" style="--stagger-index: 4">
          <div class="flex items-center gap-3 min-w-0">
            <div class="relative shrink-0">
              <div class="w-3 h-3 rounded-full" :class="connectedNodesCount > 0 ? 'bg-green-400' : 'bg-amber-400'"></div>
              <div v-if="connectedNodesCount > 0" class="absolute inset-0 w-3 h-3 rounded-full bg-green-400 animate-pulse opacity-75"></div>
            </div>
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium text-white">{{ t('web5.connectedNodes') }}</p>
              <p class="text-xs text-white/60">{{ t('web5.peersKnown', { count: connectedNodesCount }) }}</p>
            </div>
          </div>
          <div class="flex gap-2">
            <button
              @click="router.push('/dashboard/server/federation')"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ t('web5.findNodes') }}
            </button>
            <button
              @click="showSendMessageModal = true"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ t('web5.sendMessage') }}
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- Hardware Wallet Detected Banner -->
    <div v-if="detectedHwWallets.length > 0" class="mb-6 p-4 bg-orange-500/10 border border-orange-500/20 rounded-xl flex items-center gap-3">
      <div class="w-8 h-8 rounded-lg bg-orange-500/20 flex items-center justify-center flex-shrink-0">
        <svg class="w-5 h-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
        </svg>
      </div>
      <div class="flex-1 min-w-0">
        <p class="text-sm font-medium text-orange-400">{{ t('web5.hardwareWalletDetected') }}</p>
        <p class="text-xs text-white/60">
          {{ detectedHwWallets.map(d => `${d.type}${d.product ? ' (' + d.product + ')' : ''}`).join(', ') }}
        </p>
      </div>
    </div>

    <!-- DID Document Modal -->
    <Teleport to="body">
      <div v-if="showDidDocModal" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md" @click.self="showDidDocModal = false" @keydown.escape="showDidDocModal = false">
        <div class="glass-card p-6 max-w-2xl w-full max-h-[90vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="did-doc-title">
          <div class="flex items-center justify-between mb-4">
            <h3 id="did-doc-title" class="text-lg font-semibold text-white">{{ t('web5.didDocument') }}</h3>
            <div class="flex items-center gap-2">
              <span v-if="didDocVerified === true" class="text-xs text-green-400 flex items-center gap-1">
                <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
                {{ t('web5.verified') }}
              </span>
              <span v-else-if="didDocVerified === false" class="text-xs text-red-400">{{ t('web5.invalid') }}</span>
            </div>
          </div>
          <div v-if="loadingDidDoc" class="text-white/60 text-sm">{{ t('common.loading') }}</div>
          <pre v-else class="text-xs text-white/80 font-mono bg-black/30 rounded-lg p-4 overflow-x-auto whitespace-pre-wrap">{{ didDocumentFormatted }}</pre>
          <div class="flex gap-3 mt-4">
            <button
              @click="copyDidDocument"
              class="flex-1 px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ didDocCopied ? t('common.copiedBang') : t('common.copy') }}
            </button>
            <button
              @click="showDidDocModal = false"
              class="px-4 py-2 rounded-lg bg-white/10 text-white font-medium hover:bg-white/20 transition-colors"
            >
              {{ t('common.close') }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Send Message Modal -->
    <Teleport to="body">
      <div v-if="showSendMessageModal" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md" @click.self="closeSendMessageModal()">
        <div ref="sendMessageModalRef" class="glass-card p-6 max-w-md w-full max-h-[90vh] overflow-y-auto">
          <h3 class="text-lg font-semibold text-white mb-4">{{ t('web5.sendMessageTitle') }}</h3>
          <p class="text-white/70 text-sm mb-4">Messages are sent over the Tor network to the selected peer.</p>
          <div class="space-y-4">
            <div>
              <label class="block text-sm font-medium text-white/80 mb-2">{{ t('web5.to') }}</label>
              <select
                v-model="sendMessageTo"
                class="w-full px-3 py-2 rounded-lg bg-white/10 text-white border border-white/20 focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
              >
                <option value="">{{ t('web5.selectPeer') }}</option>
                <option v-for="p in peers" :key="p.pubkey" :value="p.onion">
                  {{ p.name || p.onion || (p.pubkey || '').slice(0, 12) + '...' }}
                </option>
              </select>
            </div>
            <div>
              <label class="block text-sm font-medium text-white/80 mb-2">{{ t('web5.message') }}</label>
              <textarea
                v-model="sendMessageText"
                rows="3"
                class="w-full px-3 py-2 rounded-lg bg-white/10 text-white border border-white/20 focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
                :placeholder="t('web5.messagePlaceholder')"
              ></textarea>
            </div>
          </div>
          <div class="flex gap-3 mt-6">
            <button
              @click="sendMessage"
              :disabled="!sendMessageTo || !sendMessageText.trim() || sendingMessage"
              class="flex-1 px-4 py-2 rounded-lg bg-orange-500 text-white font-medium hover:bg-orange-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
            >
              {{ sendingMessage ? t('common.sending') : t('common.send') }}
            </button>
            <button
              @click="closeSendMessageModal()"
              class="px-4 py-2 rounded-lg bg-white/10 text-white font-medium hover:bg-white/20 transition-colors"
            >
              {{ t('common.cancel') }}
            </button>
          </div>
          <p v-if="sendMessageError" class="mt-3 text-sm text-red-400">{{ sendMessageError }}</p>
          <p v-if="sendMessageSuccess" class="mt-3 text-sm text-green-400">{{ sendMessageSuccess }}</p>
        </div>
      </div>
    </Teleport>

    <!-- Core Services Overview Cards — Row 1 -->
    <div class="flex flex-col md:flex-row gap-6 mb-6">
      <!-- Bitcoin Domain Name Portfolio -->
      <div data-controller-container tabindex="0" class="glass-card card-stagger p-6 flex flex-col md:w-1/2" style="--stagger-index: 0">
        <div class="flex items-start gap-4 mb-4 shrink-0">
          <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
            </svg>
          </div>
          <div class="flex-1">
            <h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.bitcoinDomains') }}</h2>
            <p class="text-white/70 text-sm mb-4">{{ t('web5.domainsSubtitle') }}</p>
          </div>
        </div>

        <div class="space-y-3 flex-1 min-h-0">
          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.namesRegistered') }}</span>
            </div>
            <span class="text-white/60 text-sm">{{ registeredNames.length }} {{ registeredNames.length === 1 ? 'name' : 'names' }}</span>
          </div>

          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('common.status') }}</span>
            </div>
            <span :class="activeNamesCount > 0 ? 'text-green-400' : 'text-white/60'" class="text-sm font-medium">
              {{ activeNamesCount > 0 ? `${activeNamesCount} Active` : 'None' }}
            </span>
          </div>

          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.expiringSoon') }}</span>
            </div>
            <span class="text-white/60 text-sm">{{ expiringNamesCount }} {{ expiringNamesCount === 1 ? 'name' : 'names' }}</span>
          </div>
        </div>

        <button @click="showDomainsModal = true" class="mt-6 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors shrink-0">
          {{ t('web5.manageDomains') }}
        </button>
      </div>

      <!-- Wallet -->
      <div data-controller-container tabindex="0" class="glass-card card-stagger p-6 flex flex-col md:w-1/2" style="--stagger-index: 1">
        <div class="flex items-start gap-4 mb-4 shrink-0">
          <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 9V7a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2m2 4h10a2 2 0 002-2v-6a2 2 0 00-2-2H9a2 2 0 00-2 2v6a2 2 0 002 2zm7-5a2 2 0 11-4 0 2 2 0 014 0z" />
            </svg>
          </div>
          <div class="flex-1">
            <h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.wallet') }}</h2>
            <p class="text-white/70 text-sm mb-4">{{ t('web5.walletSubtitle') }}</p>
          </div>
        </div>

        <div class="space-y-3 flex-1 min-h-0">
          <!-- On-chain Balance -->
          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <span class="text-lg text-orange-500 font-bold">₿</span>
              <span class="text-white/80 text-sm">{{ t('web5.onChain') }}</span>
            </div>
            <span class="text-orange-500 text-sm font-medium">{{ lndOnchainBalance.toLocaleString() }} sats</span>
          </div>

          <!-- Lightning Balance -->
          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-yellow-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.lightning') }}</span>
            </div>
            <span class="text-yellow-400 text-sm font-medium">{{ lndChannelBalance.toLocaleString() }} sats</span>
          </div>

          <!-- Ecash Balance -->
          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-purple-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.ecash') }}</span>
            </div>
            <span class="text-purple-400 text-sm font-medium">{{ ecashBalance.toLocaleString() }} sats</span>
          </div>
        </div>

        <!-- Action buttons -->
        <div class="grid grid-cols-2 gap-2 mt-auto pt-4 shrink-0">
          <button
            @click="showUnifiedSendModal = true"
            :disabled="!walletConnected && ecashBalance <= 0"
            class="px-3 py-2 glass-button rounded-lg text-xs font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
          >
            {{ t('common.send') }}
          </button>
          <button
            @click="showUnifiedReceiveModal = true"
            class="px-3 py-2 glass-button rounded-lg text-xs font-medium text-white/90 hover:text-white transition-colors"
          >
            {{ t('web5.receiveBitcoin') }}
          </button>
        </div>
      </div>
    </div>

    <!-- Core Services Overview Cards — Row 2 -->
    <div class="flex flex-col md:flex-row gap-6 mb-8">
      <!-- Nostr Relays -->
      <div data-controller-container tabindex="0" class="glass-card card-stagger p-6 flex flex-col md:w-1/2" style="--stagger-index: 2">
        <div class="flex items-start gap-4 mb-4 shrink-0">
          <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0" />
            </svg>
          </div>
          <div class="flex-1">
            <h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.nostrRelays') }}</h2>
            <p class="text-white/70 text-sm mb-4">{{ t('web5.nostrRelaysDesc') }}</p>
          </div>
        </div>

        <div class="space-y-3 flex-1 min-h-0">
          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.relaysConnectedLabel') }}</span>
            </div>
            <span class="text-white/60 text-sm">{{ nostrRelayStats?.connected_count ?? 0 }} active</span>
          </div>

          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('web5.totalRelays') }}</span>
            </div>
            <span :class="(nostrRelayStats?.total_relays ?? 0) > 0 ? 'text-green-400' : 'text-white/60'" class="text-sm font-medium">
              {{ nostrRelayStats?.total_relays ?? 0 }} configured
            </span>
          </div>

          <div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3">
              <svg class="w-5 h-5 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
              </svg>
              <span class="text-white/80 text-sm">{{ t('common.enabled') }}</span>
            </div>
            <span class="text-white/60 text-sm">{{ nostrRelayStats?.enabled_count ?? 0 }} relays</span>
          </div>
        </div>

        <button @click="showRelaysModal = true" class="mt-6 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors shrink-0">
          {{ t('web5.relays') }}
        </button>
      </div>

      <!-- Node Visibility -->
      <div data-controller-container tabindex="0" class="glass-card card-stagger p-6 flex flex-col md:w-1/2" style="--stagger-index: 3">
        <div class="flex items-start gap-4 mb-4 shrink-0">
          <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
            </svg>
          </div>
          <div class="flex-1">
            <h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.nodeVisibility') }}</h2>
            <p class="text-white/70 text-sm mb-4">{{ t('web5.nodeVisibilityDesc') }}</p>
          </div>
          <div v-if="visibilityLoading" class="shrink-0">
            <svg class="animate-spin h-5 w-5 text-white/40" fill="none" viewBox="0 0 24 24">
              <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
              <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
            </svg>
          </div>
        </div>

        <!-- Visibility Options -->
        <div class="space-y-2 flex-1 min-h-0">
          <button
            v-for="opt in visibilityOptions"
            :key="opt.value"
            @click="setVisibility(opt.value)"
            :disabled="settingVisibility"
            class="w-full flex items-center gap-3 p-3 rounded-lg border transition-colors text-left"
            :class="nodeVisibility === opt.value
              ? 'bg-white/10 border-white/25 text-white'
              : 'bg-white/5 border-white/10 text-white/60 hover:bg-white/8 hover:text-white/80'"
          >
            <div class="w-3 h-3 rounded-full shrink-0 border-2 flex items-center justify-center"
              :class="nodeVisibility === opt.value ? 'border-green-400' : 'border-white/30'"
            >
              <div v-if="nodeVisibility === opt.value" class="w-1.5 h-1.5 rounded-full bg-green-400"></div>
            </div>
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium">{{ opt.label }}</p>
              <p class="text-xs text-white/50">{{ opt.description }}</p>
            </div>
          </button>
        </div>

        <!-- Onion address (shown when discoverable/public) -->
        <div v-if="nodeVisibility !== 'hidden' && nodeOnionAddress" class="mt-4 p-3 bg-white/5 rounded-lg">
          <div class="flex items-center justify-between gap-2">
            <div class="min-w-0">
              <p class="text-xs text-white/50 mb-1">{{ t('web5.yourTorAddress') }}</p>
              <p class="text-xs font-mono text-white/80 truncate" :title="nodeOnionAddress">{{ nodeOnionAddress }}</p>
            </div>
            <button @click="copyOnionAddress" class="shrink-0 p-2 rounded-lg text-white/50 hover:text-white hover:bg-white/10 transition-colors" title="Copy">
              <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="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
              </svg>
            </button>
          </div>
        </div>

        <!-- Warning -->
        <p v-if="nodeVisibility !== 'hidden'" class="mt-3 text-xs text-amber-400/80">
          {{ t('web5.discoverableWarning') }}
        </p>
      </div>
    </div>

    <!-- Connected Nodes (P2P over Tor) -->
    <div ref="nodesContainerRef" data-controller-container tabindex="0" class="glass-card p-6 mb-8 scroll-mt-24">
        <!-- Desktop: side-by-side layout -->
        <div class="hidden md:flex items-start gap-4 mb-4">
          <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
            </svg>
          </div>
          <div class="flex-1">
            <h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.connectedNodes') }}</h2>
            <p class="text-white/70 text-sm mb-4">{{ t('web5.peerNodesDescription') }}</p>
          </div>
          <div class="flex gap-2 shrink-0">
            <button
              @click="router.push('/dashboard/server/federation')"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ t('web5.findNodes') }}
            </button>
            <button
              @click="loadPeers"
              class="px-3 py-1.5 glass-button glass-button-sm rounded text-xs font-medium text-white/90 hover:text-white transition-colors"
            >
              {{ loadingPeers ? '...' : t('common.refresh') }}
            </button>
          </div>
        </div>
        <!-- Mobile: stacked layout -->
        <div class="md:hidden mb-4">
          <div class="flex items-center gap-4 mb-2">
            <div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
              <svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
              </svg>
            </div>
            <h2 class="text-xl font-semibold text-white">{{ t('web5.connectedNodes') }}</h2>
          </div>
          <p class="text-white/70 text-sm mb-3">{{ t('web5.peerNodesDescription') }}</p>
          <div class="grid grid-cols-2 gap-2">
            <button
              @click="router.push('/dashboard/server/federation')"
              class="min-h-[44px] glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors flex items-center justify-center"
            >
              {{ t('web5.findNodes') }}
            </button>
            <button
              @click="loadPeers"
              class="min-h-[44px] glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors flex items-center justify-center"
            >
              {{ loadingPeers ? '...' : t('common.refresh') }}
            </button>
          </div>
        </div>

        <!-- Tabs: Peers | Messages | Requests -->
        <div class="flex gap-1 mb-4 border-b border-white/10">
          <button
            @click="nodesContainerTab = 'peers'"
            class="px-4 py-2 text-sm font-medium rounded-t-lg transition-colors"
            :class="nodesContainerTab === 'peers' ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white/80 hover:bg-white/5'"
          >
            {{ t('web5.peers') }}
            <span v-if="peers.length > 0" class="ml-1.5 text-xs text-white/50">({{ peers.length }})</span>
          </button>
          <button
            @click="switchToMessagesTab"
            class="px-4 py-2 text-sm font-medium rounded-t-lg transition-colors flex items-center gap-1.5"
            :class="nodesContainerTab === 'messages' ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white/80 hover:bg-white/5'"
          >
            {{ t('web5.messages') }}
            <span v-if="receivedMessages.length > 0" class="ml-1.5 text-xs" :class="unreadCount > 0 ? 'text-orange-400' : 'text-white/50'">({{ receivedMessages.length }})</span>
            <span v-if="unreadCount > 0" class="w-2 h-2 rounded-full bg-orange-500 animate-pulse"></span>
          </button>
          <button
            @click="switchToRequestsTab"
            class="px-4 py-2 text-sm font-medium rounded-t-lg transition-colors flex items-center gap-1.5"
            :class="nodesContainerTab === 'requests' ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white/80 hover:bg-white/5'"
          >
            {{ t('web5.requests') }}
            <span v-if="connectionRequests.length > 0" class="ml-1.5 text-xs text-orange-400">({{ connectionRequests.length }})</span>
            <span v-if="connectionRequests.length > 0" class="w-2 h-2 rounded-full bg-orange-500 animate-pulse"></span>
          </button>
        </div>

        <!-- Peers tab -->
        <div v-show="nodesContainerTab === 'peers'" class="space-y-2 max-h-48 overflow-y-auto">
          <div v-if="peers.length === 0" class="p-4 text-center text-white/60 text-sm">
            {{ t('web5.noPeers') }}
          </div>
          <div
            v-for="p in peers"
            :key="p.pubkey"
            class="flex items-center justify-between p-3 bg-white/5 rounded-lg"
          >
            <div class="flex items-center gap-3 min-w-0">
              <div class="w-2 h-2 rounded-full shrink-0" :class="peerReachable[p.onion] ? 'bg-green-400' : 'bg-amber-400'"></div>
              <div class="min-w-0">
                <p class="text-sm font-mono text-white/90 truncate">{{ p.name || p.onion || (p.pubkey || '').slice(0, 16) + '...' }}</p>
                <p class="text-xs text-white/50 truncate">{{ p.onion }}</p>
              </div>
            </div>
            <button
              @click="showSendMessageModal = true; sendMessageTo = p.onion"
              class="px-2 py-1 text-xs rounded bg-orange-500/20 text-orange-400 hover:bg-orange-500/30 transition-colors shrink-0"
            >
              {{ t('web5.message') }}
            </button>
          </div>
        </div>

        <!-- Messages tab -->
        <div v-show="nodesContainerTab === 'messages'" class="space-y-2 max-h-64 overflow-y-auto">
          <div v-if="loadingMessages" class="p-4 text-center text-white/60 text-sm">
            {{ t('common.loading') }}
          </div>
          <div v-else-if="receivedMessages.length === 0" class="p-4 text-center text-white/60 text-sm">
            {{ t('web5.noMessages') }}
          </div>
          <div
            v-for="(m, idx) in receivedMessages"
            :key="idx"
            class="p-3 bg-white/5 rounded-lg border-l-2 border-orange-500/50"
          >
            <div class="flex items-center justify-between gap-2 mb-1">
              <p class="text-xs font-mono text-white/60 truncate" :title="m.from_pubkey">{{ (m.from_pubkey || '').slice(0, 16) }}...</p>
              <span class="text-xs text-white/40 shrink-0">{{ formatMessageTime(m.timestamp) }}</span>
            </div>
            <p class="text-sm text-white/90 break-words">{{ m.message }}</p>
          </div>
        </div>

        <!-- Requests tab -->
        <div v-show="nodesContainerTab === 'requests'" class="space-y-2 max-h-64 overflow-y-auto">
          <div v-if="loadingRequests" class="p-4 text-center text-white/60 text-sm">
            {{ t('common.loading') }}
          </div>
          <div v-else-if="connectionRequests.length === 0" class="p-4 text-center text-white/60 text-sm">
            {{ t('web5.noRequests') }}
          </div>
          <div
            v-for="req in connectionRequests"
            :key="req.id"
            class="p-3 bg-white/5 rounded-lg border-l-2 border-blue-500/50"
          >
            <div class="flex items-start justify-between gap-3">
              <div class="min-w-0 flex-1">
                <p class="text-xs font-mono text-white/70 truncate" :title="req.from_did">{{ req.from_did }}</p>
                <p v-if="req.message" class="text-sm text-white/80 mt-1 break-words">{{ req.message }}</p>
                <p class="text-xs text-white/40 mt-1">{{ formatMessageTime(req.created_at) }}</p>
              </div>
              <div class="flex items-center gap-2 shrink-0">
                <button
                  @click="acceptRequest(req.id)"
                  :disabled="processingRequestId === req.id"
                  class="px-3 py-1.5 text-xs rounded-lg bg-green-500/20 text-green-400 hover:bg-green-500/30 transition-colors disabled:opacity-50"
                >
                  {{ t('web5.accept') }}
                </button>
                <button
                  @click="rejectRequest(req.id)"
                  :disabled="processingRequestId === req.id"
                  class="px-3 py-1.5 text-xs rounded-lg bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors disabled:opacity-50"
                >
                  {{ t('web5.reject') }}
                </button>
              </div>
            </div>
          </div>
        </div>

        <button
          v-if="nodesContainerTab === 'peers'"
          @click="discoverAndAddPeers"
          :disabled="discovering"
          class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
        >
          {{ discovering ? t('web5.discovering') : t('web5.discoverNodes') }}
        </button>
        <button
          v-else-if="nodesContainerTab === 'messages'"
          @click="loadReceivedMessages"
          :disabled="loadingMessages"
          class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
        >
          {{ loadingMessages ? t('common.loading') : t('web5.refreshMessages') }}
        </button>
        <button
          v-else
          @click="loadConnectionRequests"
          :disabled="loadingRequests"
          class="mt-4 w-full px-4 py-2 glass-button rounded-lg text-sm font-medium text-white/90 hover:text-white transition-colors disabled:opacity-50"
        >
          {{ loadingRequests ? t('common.loading') : t('web5.refreshRequests') }}
        </button>
      </div>

    <!-- Shared Content -->
    <div class="glass-card p-6 mb-8">
      <!-- Desktop: side-by-side -->
      <div class="hidden md:flex items-center justify-between mb-4">
        <div class="flex items-center gap-3">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
            </svg>
          </div>
          <div>
            <h2 class="text-lg font-semibold text-white">{{ t('web5.content') }}</h2>
            <p class="text-xs text-white/60">{{ t('web5.contentDesc') }}</p>
          </div>
        </div>
        <div v-if="contentTab === 'mine'" class="flex items-center gap-2">
          <button @click="loadContentItems" :disabled="contentLoading" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium">
            {{ contentLoading ? '...' : 'Refresh' }}
          </button>
          <button @click="showAddContentModal = true" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium flex items-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="M12 4v16m8-8H4" />
            </svg>
            Add
          </button>
        </div>
      </div>
      <!-- Mobile: stacked -->
      <div class="md:hidden mb-4">
        <div class="flex items-center gap-3 mb-2">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
            </svg>
          </div>
          <h2 class="text-lg font-semibold text-white">{{ t('web5.content') }}</h2>
        </div>
        <p class="text-xs text-white/60 mb-3">{{ t('web5.contentDesc') }}</p>
        <div v-if="contentTab === 'mine'" class="grid grid-cols-2 gap-2">
          <button @click="loadContentItems" :disabled="contentLoading" class="glass-button min-h-[44px] rounded-lg text-sm font-medium flex items-center justify-center">
            {{ contentLoading ? '...' : 'Refresh' }}
          </button>
          <button @click="showAddContentModal = true" class="glass-button min-h-[44px] rounded-lg text-sm font-medium 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="M12 4v16m8-8H4" />
            </svg>
            Add
          </button>
        </div>
      </div>

      <!-- Tabs: My Content | Browse Peers -->
      <div class="flex gap-1 mb-4 border-b border-white/10">
        <button
          @click="contentTab = 'mine'"
          class="px-4 py-2 text-sm font-medium rounded-t-lg transition-colors"
          :class="contentTab === 'mine' ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white/80 hover:bg-white/5'"
        >
          {{ t('web5.myContent') }}
          <span v-if="contentItems.length > 0" class="ml-1.5 text-xs text-white/50">({{ contentItems.length }})</span>
        </button>
        <button
          @click="contentTab = 'browse'"
          class="px-4 py-2 text-sm font-medium rounded-t-lg transition-colors"
          :class="contentTab === 'browse' ? 'bg-white/10 text-white' : 'text-white/60 hover:text-white/80 hover:bg-white/5'"
        >
          {{ t('web5.browsePeers') }}
        </button>
      </div>

      <!-- My Content tab -->
      <div v-show="contentTab === 'mine'">
        <!-- Loading -->
        <div v-if="contentLoading && contentItems.length === 0" class="py-4 text-center">
          <svg class="animate-spin h-6 w-6 text-blue-400 mx-auto mb-2" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <p class="text-white/50 text-sm">{{ t('common.loading') }}</p>
        </div>

        <!-- Empty -->
        <div v-else-if="contentItems.length === 0" class="py-6 text-center">
          <svg class="w-12 h-12 text-white/20 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 4v16M17 4v16M3 8h4m10 0h4M3 12h18M3 16h4m10 0h4M4 20h16a1 1 0 001-1V5a1 1 0 00-1-1H4a1 1 0 00-1 1v14a1 1 0 001 1z" />
          </svg>
          <p class="text-white/60 text-sm mb-1">{{ t('web5.noSharedContent') }}</p>
          <p class="text-white/40 text-xs">{{ t('web5.addFilesToShare') }}</p>
        </div>

        <!-- Content List -->
        <div v-else class="space-y-3">
        <div
          v-for="(item, idx) in contentItems"
          :key="item.id"
          class="card-stagger p-4 bg-white/5 rounded-lg"
          :style="{ '--stagger-index': idx }"
        >
          <div class="flex items-start justify-between gap-3 mb-3">
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium text-white truncate">{{ item.filename }}</p>
              <p v-if="item.description" class="text-xs text-white/50 mt-0.5">{{ item.description }}</p>
              <p class="text-xs text-white/40 mt-0.5">{{ item.mime_type }} &middot; {{ formatBytes(item.size_bytes) }}</p>
            </div>
            <button
              @click="removeContentItem(item.id)"
              :disabled="removingContentId === item.id"
              class="p-2 rounded-lg text-white/40 hover:text-red-400 hover:bg-white/10 transition-colors shrink-0"
              title="Remove"
            >
              <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
              </svg>
            </button>
          </div>

          <!-- Pricing Controls -->
          <div class="flex flex-wrap items-center gap-2 mb-2">
            <button
              v-for="opt in accessOptions"
              :key="opt.value"
              @click="setContentPricing(item, opt.value)"
              :disabled="updatingPricingId === item.id"
              class="px-3 py-1 text-xs rounded-lg border transition-colors"
              :class="getAccessType(item) === opt.value
                ? 'bg-white/15 border-white/30 text-white'
                : 'bg-white/5 border-white/10 text-white/50 hover:bg-white/10 hover:text-white/70'"
            >
              {{ opt.label }}
            </button>
          </div>

          <!-- Price Input (visible when "paid" is selected) -->
          <div v-if="getAccessType(item) === 'paid'" class="flex items-center gap-3 mt-2">
            <div class="flex items-center gap-2 flex-1">
              <input
                :value="getItemPrice(item)"
                @change="updateItemPrice(item, ($event.target as HTMLInputElement).value)"
                type="number"
                min="1"
                placeholder="100"
                class="w-24 px-2 py-1 text-xs rounded-lg bg-white/5 border border-white/10 text-white focus:outline-none focus:border-white/30"
              />
              <span class="text-xs text-white/50">sats</span>
            </div>
            <p class="text-xs text-orange-400/80">Peers will pay {{ getItemPrice(item) || 0 }} sats to access this</p>
          </div>

          <!-- Free label -->
          <p v-else-if="getAccessType(item) === 'free'" class="text-xs text-green-400/70 mt-1">{{ t('web5.freeAccessDesc') }}</p>
          <p v-else-if="getAccessType(item) === 'peers_only'" class="text-xs text-blue-400/70 mt-1">{{ t('web5.peersOnlyAccessDesc') }}</p>
        </div>
      </div>
      </div>

      <!-- Browse Peers tab -->
      <div v-show="contentTab === 'browse'">
        <!-- Peer Selector -->
        <div class="mb-4">
          <div class="flex items-center gap-3">
            <select
              v-model="browsePeerOnion"
              class="flex-1 px-3 py-2 rounded-lg bg-white/10 text-white text-sm border border-white/20 focus:border-orange-500 focus:ring-1 focus:ring-orange-500"
            >
              <option value="">{{ t('web5.selectPeer') }}</option>
              <option v-for="p in peers" :key="p.pubkey" :value="p.onion">
                {{ p.name || p.onion || (p.pubkey || '').slice(0, 12) + '...' }}
              </option>
            </select>
            <button
              @click="browsePeerContent"
              :disabled="!browsePeerOnion || browsingPeerContent"
              class="glass-button glass-button-sm px-4 py-2 rounded-lg text-sm font-medium disabled:opacity-50"
            >
              {{ browsingPeerContent ? t('common.loading') : t('web5.browse') }}
            </button>
          </div>
          <p v-if="browsePeerError" class="text-xs text-red-400 mt-2">{{ browsePeerError }}</p>
        </div>

        <!-- Peer Content Loading -->
        <div v-if="browsingPeerContent" class="py-4 text-center">
          <svg class="animate-spin h-6 w-6 text-blue-400 mx-auto mb-2" fill="none" viewBox="0 0 24 24">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <p class="text-white/50 text-sm">{{ t('web5.connectingToPeer') }}</p>
        </div>

        <!-- No peer selected -->
        <div v-else-if="!browsePeerOnion && peerContentItems.length === 0" class="py-6 text-center">
          <svg class="w-12 h-12 text-white/20 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
          </svg>
          <p class="text-white/60 text-sm mb-1">{{ t('web5.selectPeerToBrowse') }}</p>
          <p class="text-white/40 text-xs">{{ t('web5.choosePeerDesc') }}</p>
        </div>

        <!-- Peer has no content -->
        <div v-else-if="peerContentItems.length === 0 && browsePeerOnion && !browsingPeerContent" class="py-6 text-center">
          <p class="text-white/60 text-sm">{{ t('web5.peerNoContent') }}</p>
        </div>

        <!-- Peer Content List -->
        <div v-else class="space-y-2">
          <div
            v-for="(pItem, idx) in peerContentItems"
            :key="pItem.id"
            class="card-stagger flex items-center gap-4 p-3 bg-white/5 rounded-lg"
            :style="{ '--stagger-index': idx }"
          >
            <!-- Media type icon -->
            <div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center shrink-0">
              <svg v-if="isMediaType(pItem.mime_type)" class="w-4 h-4 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
              </svg>
              <svg v-else class="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
              </svg>
            </div>

            <div class="flex-1 min-w-0">
              <p class="text-sm font-medium text-white truncate">{{ pItem.filename }}</p>
              <p v-if="pItem.description" class="text-xs text-white/50 truncate">{{ pItem.description }}</p>
              <div class="flex items-center gap-2 mt-0.5">
                <span class="text-xs text-white/40">{{ pItem.mime_type }}</span>
                <span class="text-xs text-white/30">&middot;</span>
                <span class="text-xs text-white/40">{{ formatBytes(pItem.size_bytes) }}</span>
                <span v-if="getPeerItemPrice(pItem) > 0" class="text-xs text-orange-400 ml-1">{{ getPeerItemPrice(pItem) }} sats</span>
                <span v-else class="text-xs text-green-400/70 ml-1">Free</span>
              </div>
            </div>

            <!-- Stream/Download button -->
            <button
              v-if="isMediaType(pItem.mime_type)"
              @click="streamPeerContent(pItem)"
              class="px-3 py-1.5 text-xs rounded-lg bg-orange-500/20 text-orange-400 hover:bg-orange-500/30 transition-colors shrink-0"
            >
              {{ t('web5.stream') }}
            </button>
            <button
              v-else
              @click="downloadPeerContent(pItem)"
              class="px-3 py-1.5 text-xs rounded-lg bg-blue-500/20 text-blue-400 hover:bg-blue-500/30 transition-colors shrink-0"
            >
              {{ t('web5.download') }}
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- Content Streaming Player -->
    <Teleport to="body">
      <div v-if="streamingItem" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-md" @click.self="closePlayer" @keydown.escape="closePlayer">
        <div class="glass-card p-0 w-full max-w-2xl overflow-hidden" role="dialog" aria-modal="true">
          <!-- Player Header -->
          <div class="flex items-center justify-between px-4 py-3 border-b border-white/10">
            <div class="min-w-0 flex-1">
              <p class="text-sm font-medium text-white truncate">{{ streamingItem.filename }}</p>
              <p class="text-xs text-white/50">{{ streamingItem.mime_type }}</p>
            </div>
            <div class="flex items-center gap-3 shrink-0">
              <div v-if="streamCostSats > 0" class="flex items-center gap-1 px-2 py-1 rounded bg-orange-500/20">
                <span class="text-xs text-orange-400 font-medium">{{ streamCostSats }} sats</span>
              </div>
              <button @click="closePlayer" class="p-2 rounded-lg text-white/50 hover:text-white hover:bg-white/10 transition-colors">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
                </svg>
              </button>
            </div>
          </div>

          <!-- Player Body -->
          <div class="p-4">
            <!-- Audio Player -->
            <div v-if="streamingItem.mime_type.startsWith('audio/')">
              <audio
                ref="audioPlayerRef"
                :src="streamUrl"
                controls
                class="w-full"
                @timeupdate="onPlayerTimeUpdate"
                @error="onPlayerError"
              ></audio>
            </div>

            <!-- Video Player -->
            <div v-else-if="streamingItem.mime_type.startsWith('video/')">
              <video
                ref="videoPlayerRef"
                :src="streamUrl"
                controls
                class="w-full rounded-lg max-h-[60vh]"
                @timeupdate="onPlayerTimeUpdate"
                @error="onPlayerError"
              ></video>
            </div>

            <!-- Player Error -->
            <div v-if="playerError" class="mt-3 p-3 bg-red-500/10 border border-red-500/20 rounded-lg">
              <p class="text-red-400 text-sm">{{ playerError }}</p>
              <p class="text-white/50 text-xs mt-1">This may be a Tor-only resource. Copy the URL to use with a Tor-enabled media player.</p>
            </div>

            <!-- Stream Info -->
            <div class="flex items-center justify-between mt-3">
              <div class="text-xs text-white/40">
                {{ formatBytes(streamingItem.size_bytes) }}
                <span v-if="streamProgress > 0"> &middot; {{ Math.round(streamProgress * 100) }}% streamed</span>
              </div>
              <button
                @click="copyStreamUrl"
                class="text-xs text-white/50 hover:text-white transition-colors"
              >
                Copy URL
              </button>
            </div>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Add Content Modal -->
    <Teleport to="body">
      <div v-if="showAddContentModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showAddContentModal = false" @keydown.escape="showAddContentModal = false">
        <div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="add-content-title">
          <h2 id="add-content-title" class="text-lg font-bold text-white mb-4">{{ t('web5.addContentTitle') }}</h2>
          <div class="space-y-4">
            <div>
              <label class="text-white/60 text-sm block mb-1">Filename</label>
              <input v-model="newContentFilename" type="text" placeholder="my-file.mp3" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            </div>
            <div>
              <label class="text-white/60 text-sm block mb-1">MIME Type</label>
              <input v-model="newContentMimeType" type="text" placeholder="audio/mpeg" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            </div>
            <div>
              <label class="text-white/60 text-sm block mb-1">Description (optional)</label>
              <input v-model="newContentDescription" type="text" placeholder="A short description" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            </div>
            <div>
              <label class="text-white/60 text-sm block mb-2">Access</label>
              <div class="flex gap-2">
                <button
                  v-for="opt in accessOptions"
                  :key="opt.value"
                  @click="newContentAccess = opt.value"
                  class="px-3 py-1.5 text-xs rounded-lg border transition-colors"
                  :class="newContentAccess === opt.value
                    ? 'bg-white/15 border-white/30 text-white'
                    : 'bg-white/5 border-white/10 text-white/50 hover:bg-white/10'"
                >{{ opt.label }}</button>
              </div>
            </div>
            <div v-if="newContentAccess === 'paid'">
              <label class="text-white/60 text-sm block mb-1">Price (sats)</label>
              <input v-model.number="newContentPrice" type="number" min="1" placeholder="100" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
              <p v-if="newContentPrice > 0" class="text-xs text-orange-400/80 mt-1">Peers will pay {{ newContentPrice }} sats to access this</p>
            </div>
          </div>
          <div v-if="addContentError" class="mt-3 p-2 bg-red-500/20 border border-red-500/30 rounded-lg">
            <p class="text-red-300 text-xs">{{ addContentError }}</p>
          </div>
          <div class="flex gap-3 mt-6">
            <button @click="showAddContentModal = false; addContentError = ''" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">{{ t('common.cancel') }}</button>
            <button @click="addContentItem" :disabled="addingContent || !newContentFilename.trim()" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-orange-500/20 border-orange-500/30 disabled:opacity-50">
              {{ addingContent ? 'Adding...' : 'Add' }}
            </button>
          </div>
        </div>
      </div>
    </Teleport>

    <!-- Identity Management -->
    <div class="glass-card p-6 mb-8">
      <!-- Desktop: side-by-side -->
      <div class="hidden md:flex items-center justify-between mb-4">
        <div class="flex items-center gap-3">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2" />
            </svg>
          </div>
          <div>
            <h2 class="text-lg font-semibold text-white">{{ t('web5.identities') }}</h2>
            <p class="text-xs text-white/60">{{ t('web5.identitiesDesc') }}</p>
          </div>
        </div>
        <button @click="showCreateIdentityModal = true" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium flex items-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="M12 4v16m8-8H4" />
          </svg>
          Create
        </button>
      </div>
      <!-- Mobile: stacked -->
      <div class="md:hidden mb-4">
        <div class="flex items-center gap-3 mb-2">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2" />
            </svg>
          </div>
          <h2 class="text-lg font-semibold text-white">{{ t('web5.identities') }}</h2>
        </div>
        <p class="text-xs text-white/60 mb-3">{{ t('web5.identitiesDesc') }}</p>
        <button @click="showCreateIdentityModal = true" class="w-full min-h-[44px] glass-button rounded-lg text-sm font-medium 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="M12 4v16m8-8H4" />
          </svg>
          Create
        </button>
      </div>

      <!-- Loading -->
      <div v-if="identitiesLoading" class="py-6 text-center">
        <svg class="animate-spin h-6 w-6 text-blue-400 mx-auto mb-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
          <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
          <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
        </svg>
        <p class="text-white/50 text-sm">{{ t('common.loading') }}</p>
      </div>

      <!-- Empty State -->
      <div v-else-if="managedIdentities.length === 0" class="py-6 text-center">
        <svg class="w-12 h-12 text-white/20 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
        </svg>
        <p class="text-white/60 text-sm mb-1">{{ t('web5.noIdentities') }}</p>
        <p class="text-white/40 text-xs">{{ t('web5.createFirstIdentity') }}</p>
      </div>

      <!-- Identity List -->
      <div v-else class="space-y-3">
        <div
          v-for="(identity, idx) in managedIdentities"
          :key="identity.id"
          class="card-stagger flex items-center gap-4 p-4 bg-white/5 rounded-lg"
          :style="{ '--stagger-index': idx }"
        >
          <!-- Purpose Icon -->
          <div class="flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center" :class="{
            'bg-blue-500/20': identity.purpose === 'personal',
            'bg-orange-500/20': identity.purpose === 'business',
            'bg-purple-500/20': identity.purpose === 'anonymous',
          }">
            <svg class="w-5 h-5" :class="{
              'text-blue-400': identity.purpose === 'personal',
              'text-orange-400': identity.purpose === 'business',
              'text-purple-400': identity.purpose === 'anonymous',
            }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
            </svg>
          </div>

          <!-- Info -->
          <div class="flex-1 min-w-0">
            <div class="flex items-center gap-2">
              <span class="text-white font-medium text-sm">{{ identity.name }}</span>
              <span v-if="identity.is_default" class="text-yellow-400 text-xs" title="Default identity">&#9733;</span>
              <span class="text-xs px-2 py-0.5 rounded-full capitalize" :class="{
                'bg-blue-500/20 text-blue-300': identity.purpose === 'personal',
                'bg-orange-500/20 text-orange-300': identity.purpose === 'business',
                'bg-purple-500/20 text-purple-300': identity.purpose === 'anonymous',
              }">{{ identity.purpose }}</span>
            </div>
            <p class="text-white/50 text-xs font-mono truncate mt-0.5" :title="identity.did">{{ identity.did }}</p>
          </div>

          <!-- Actions -->
          <div class="flex items-center gap-1 shrink-0">
            <button @click="copyIdentityDid(identity.did)" class="p-2 rounded-lg text-white/50 hover:text-white hover:bg-white/10 transition-colors" title="Copy">
              <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="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
              </svg>
            </button>
            <button v-if="!identity.is_default" @click="setDefaultIdentity(identity.id)" class="p-2 rounded-lg text-white/50 hover:text-yellow-400 hover:bg-white/10 transition-colors" title="Set as default">
              <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="M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z" />
              </svg>
            </button>
            <button @click="confirmDeleteIdentity(identity)" class="p-2 rounded-lg text-white/50 hover:text-red-400 hover:bg-white/10 transition-colors" title="Delete">
              <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
              </svg>
            </button>
          </div>
        </div>
      </div>
    </div>

    <!-- Create Identity Modal -->
    <div v-if="showCreateIdentityModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showCreateIdentityModal = false" @keydown.escape="showCreateIdentityModal = false">
      <div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="create-identity-title">
        <h2 id="create-identity-title" class="text-lg font-bold text-white mb-4">{{ t('web5.createIdentityTitle') }}</h2>
        <div class="space-y-4">
          <div>
            <label class="text-white/60 text-sm block mb-1">Name</label>
            <input v-model="newIdentityName" type="text" placeholder="Personal" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
          </div>
          <div>
            <label class="text-white/60 text-sm block mb-1">Purpose</label>
            <div class="grid grid-cols-3 gap-2">
              <button
                v-for="p in ['personal', 'business', 'anonymous']"
                :key="p"
                @click="newIdentityPurpose = p"
                class="px-3 py-2 rounded-lg text-sm capitalize transition-colors border"
                :class="newIdentityPurpose === p ? 'bg-white/15 border-white/30 text-white' : 'bg-white/5 border-white/10 text-white/60 hover:bg-white/10'"
              >{{ p }}</button>
            </div>
          </div>
        </div>
        <div v-if="createIdentityError" class="mt-3 p-2 bg-red-500/20 border border-red-500/30 rounded-lg">
          <p class="text-red-300 text-xs">{{ createIdentityError }}</p>
        </div>
        <div class="flex gap-3 mt-6">
          <button @click="showCreateIdentityModal = false" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">{{ t('common.cancel') }}</button>
          <button @click="createIdentity" :disabled="creatingIdentity" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-blue-500/20 border-blue-500/30">
            {{ creatingIdentity ? t('web5.creatingDid') : t('web5.createIdentity') }}
          </button>
        </div>
      </div>
    </div>

    <!-- Delete Confirmation Modal -->
    <div v-if="deleteIdentityTarget" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="deleteIdentityTarget = null" @keydown.escape="deleteIdentityTarget = null">
      <div class="glass-card p-6 w-full max-w-sm mx-4" role="dialog" aria-modal="true" aria-labelledby="delete-identity-title">
        <h2 id="delete-identity-title" class="text-lg font-bold text-white mb-2">{{ t('web5.deleteIdentityTitle') }}</h2>
        <p class="text-white/60 text-sm mb-4">{{ t('web5.deleteIdentityConfirm') }}</p>
        <div class="flex gap-3">
          <button @click="deleteIdentityTarget = null" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">{{ t('common.cancel') }}</button>
          <button @click="deleteIdentity" :disabled="deletingIdentity" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-red-500/20 border-red-500/30">
            {{ deletingIdentity ? t('web5.deleting') : t('common.delete') }}
          </button>
        </div>
      </div>
    </div>
    <!-- Unified Send Modal -->
    <div v-if="showUnifiedSendModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="closeUnifiedSendModal" @keydown.escape="closeUnifiedSendModal">
      <div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="send-bitcoin-title">
        <h2 id="send-bitcoin-title" class="text-lg font-bold text-white mb-4">{{ t('web5.sendBitcoinTitle') }}</h2>

        <!-- Method tabs -->
        <div class="flex gap-1 mb-4 p-1 bg-white/5 rounded-lg">
          <button
            v-for="m in (['auto', 'lightning', 'onchain', 'ecash'] as const)"
            :key="m"
            @click="sendMethod = m"
            class="flex-1 px-2 py-1.5 rounded text-xs font-medium capitalize transition-colors"
            :class="sendMethod === m ? 'bg-white/15 text-white' : 'text-white/50 hover:text-white/80'"
          >{{ m === 'onchain' ? 'On-chain' : m }}</button>
        </div>

        <!-- Auto mode hint -->
        <div v-if="sendMethod === 'auto'" class="mb-3 p-2 bg-white/5 rounded-lg">
          <p class="text-xs text-white/50">Auto-selects method based on amount: ecash &lt; 1k sats, Lightning 1k–500k, on-chain &gt; 500k</p>
        </div>

        <!-- Amount -->
        <div class="mb-3">
          <label class="text-white/60 text-sm block mb-1">Amount (sats)</label>
          <input v-model.number="unifiedSendAmount" type="number" min="1" placeholder="1000" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
        </div>

        <!-- Destination (varies by method) -->
        <div v-if="effectiveSendMethod !== 'ecash'" class="mb-3">
          <label class="text-white/60 text-sm block mb-1">
            {{ effectiveSendMethod === 'lightning' ? 'Lightning Invoice (BOLT11)' : 'Bitcoin Address' }}
          </label>
          <textarea v-model="unifiedSendDest" rows="2" :placeholder="effectiveSendMethod === 'lightning' ? 'lnbc...' : 'bc1...'" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm font-mono focus:outline-none focus:border-white/30"></textarea>
        </div>

        <!-- Ecash token output -->
        <div v-if="ecashSendToken && effectiveSendMethod === 'ecash'" class="mb-3 p-2 bg-white/5 rounded-lg">
          <p class="text-white/50 text-xs mb-1">Token (share with recipient):</p>
          <p class="text-xs font-mono text-white/80 break-all">{{ ecashSendToken }}</p>
          <button @click="copyEcashToken(ecashSendToken)" class="mt-2 text-xs text-orange-400 hover:text-orange-300">Copy</button>
        </div>

        <!-- Hardware Wallet toggle (on-chain only) -->
        <div v-if="effectiveSendMethod === 'onchain'" class="mb-3 flex items-center gap-3 p-3 bg-white/5 rounded-lg">
          <label class="relative inline-flex items-center cursor-pointer">
            <input type="checkbox" v-model="useHardwareWallet" class="sr-only peer" />
            <div class="w-9 h-5 bg-white/10 peer-focus:outline-none rounded-full peer peer-checked:bg-orange-500/40 transition-colors after:content-[''] after:absolute after:top-0.5 after:left-[2px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full"></div>
          </label>
          <div>
            <p class="text-sm text-white">{{ t('web5.signWithHwWallet') }}</p>
            <p class="text-xs text-white/40">{{ t('web5.createsPsbt') }}</p>
          </div>
        </div>

        <!-- PSBT display (hardware wallet flow) -->
        <div v-if="psbtStep === 'created' && psbtData" class="mb-3 space-y-2">
          <div class="p-3 bg-white/5 rounded-lg">
            <p class="text-xs text-white/50 mb-1">Unsigned PSBT (copy or download):</p>
            <textarea readonly :value="psbtData" rows="3" class="w-full bg-black/20 border border-white/10 rounded px-2 py-1 text-xs font-mono text-white/80 focus:outline-none"></textarea>
            <div class="flex gap-2 mt-2">
              <button @click="copyPsbt" class="text-xs text-orange-400 hover:text-orange-300">Copy PSBT</button>
              <button @click="downloadPsbt" class="text-xs text-orange-400 hover:text-orange-300">Download .psbt</button>
            </div>
          </div>
          <div class="p-3 bg-white/5 rounded-lg">
            <p class="text-xs text-white/50 mb-1">Paste signed PSBT or upload file:</p>
            <textarea v-model="signedPsbtInput" rows="3" placeholder="Paste signed PSBT base64 here..." class="w-full bg-black/20 border border-white/10 rounded px-2 py-1 text-xs font-mono text-white/80 focus:outline-none focus:border-white/30"></textarea>
            <div class="flex gap-2 mt-2">
              <label class="text-xs text-orange-400 hover:text-orange-300 cursor-pointer">
                Upload .psbt
                <input type="file" accept=".psbt,.txt" class="hidden" @change="handlePsbtFileUpload" />
              </label>
            </div>
          </div>
        </div>

        <!-- On-chain txid result -->
        <div v-if="sendResultTxid" class="mb-3 p-2 bg-green-500/10 border border-green-500/20 rounded-lg">
          <p class="text-green-400 text-xs">Sent! TX: {{ sendResultTxid }}</p>
        </div>

        <!-- Lightning payment result -->
        <div v-if="sendResultHash" class="mb-3 p-2 bg-green-500/10 border border-green-500/20 rounded-lg">
          <p class="text-green-400 text-xs">Paid! Hash: {{ sendResultHash }}</p>
        </div>

        <div v-if="unifiedSendError" class="mb-3 text-xs text-red-400">{{ unifiedSendError }}</div>

        <div class="flex gap-3">
          <button @click="closeUnifiedSendModal" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">{{ t('common.close') }}</button>
          <button v-if="psbtStep === 'created'" @click="finalizePsbt" :disabled="unifiedSendProcessing || !signedPsbtInput.trim()" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-orange-500/20 border-orange-500/30 disabled:opacity-50">
            {{ unifiedSendProcessing ? 'Broadcasting...' : 'Broadcast' }}
          </button>
          <button v-else @click="unifiedSend" :disabled="unifiedSendProcessing || !unifiedSendAmount" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-orange-500/20 border-orange-500/30 disabled:opacity-50">
            {{ unifiedSendProcessing ? 'Sending...' : (useHardwareWallet && effectiveSendMethod === 'onchain' ? 'Create PSBT' : 'Send') }}
          </button>
        </div>
      </div>
    </div>

    <!-- Unified Receive Modal -->
    <div v-if="showUnifiedReceiveModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="closeUnifiedReceiveModal" @keydown.escape="closeUnifiedReceiveModal">
      <div class="glass-card p-6 w-full max-w-md mx-4" role="dialog" aria-modal="true" aria-labelledby="receive-bitcoin-title">
        <h2 id="receive-bitcoin-title" class="text-lg font-bold text-white mb-4">{{ t('web5.receiveBitcoinTitle') }}</h2>

        <!-- Method tabs -->
        <div class="flex gap-1 mb-4 p-1 bg-white/5 rounded-lg">
          <button
            v-for="m in (['lightning', 'onchain', 'ecash'] as const)"
            :key="m"
            @click="receiveMethod = m"
            class="flex-1 px-2 py-1.5 rounded text-xs font-medium capitalize transition-colors"
            :class="receiveMethod === m ? 'bg-white/15 text-white' : 'text-white/50 hover:text-white/80'"
          >{{ m === 'onchain' ? 'On-chain' : m }}</button>
        </div>

        <!-- Lightning: create invoice -->
        <div v-if="receiveMethod === 'lightning'">
          <div class="mb-3">
            <label class="text-white/60 text-sm block mb-1">Amount (sats)</label>
            <input v-model.number="receiveInvoiceAmount" type="number" min="1" placeholder="1000" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
          </div>
          <div class="mb-3">
            <label class="text-white/60 text-sm block mb-1">Memo (optional)</label>
            <input v-model="receiveInvoiceMemo" type="text" placeholder="Payment for..." class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
          </div>
          <div v-if="receiveInvoiceResult" class="mb-3 p-2 bg-white/5 rounded-lg">
            <p class="text-white/50 text-xs mb-1">Invoice (share with sender):</p>
            <p class="text-xs font-mono text-white/80 break-all">{{ receiveInvoiceResult }}</p>
            <button @click="copyToClipboard(receiveInvoiceResult, 'Invoice copied')" class="mt-2 text-xs text-orange-400 hover:text-orange-300">Copy</button>
          </div>
        </div>

        <!-- On-chain: new address -->
        <div v-if="receiveMethod === 'onchain'">
          <div v-if="receiveOnchainAddress" class="mb-3 p-3 bg-white/5 rounded-lg text-center">
            <p class="text-white/50 text-xs mb-2">Your Bitcoin address:</p>
            <p class="text-sm font-mono text-white/90 break-all">{{ receiveOnchainAddress }}</p>
            <button @click="copyToClipboard(receiveOnchainAddress, 'Address copied')" class="mt-2 text-xs text-orange-400 hover:text-orange-300">Copy</button>
          </div>
          <div v-else class="mb-3 text-center">
            <p class="text-white/50 text-sm mb-2">{{ t('web5.generateFreshAddress') }}</p>
          </div>
        </div>

        <!-- Ecash: paste token -->
        <div v-if="receiveMethod === 'ecash'">
          <div class="mb-3">
            <label class="text-white/60 text-sm block mb-1">Paste ecash token</label>
            <textarea v-model="ecashReceiveToken" rows="3" placeholder="cashuSend_..." class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30"></textarea>
          </div>
          <div v-if="ecashReceiveResult" class="mb-3 text-xs text-green-400">{{ ecashReceiveResult }}</div>
        </div>

        <div v-if="unifiedReceiveError" class="mb-3 text-xs text-red-400">{{ unifiedReceiveError }}</div>

        <div class="flex gap-3">
          <button @click="closeUnifiedReceiveModal" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm">{{ t('common.close') }}</button>
          <button @click="unifiedReceive" :disabled="unifiedReceiveProcessing" class="flex-1 glass-button px-4 py-2 rounded-lg text-sm font-medium bg-green-500/20 border-green-500/30 disabled:opacity-50">
            {{ unifiedReceiveProcessing ? 'Processing...' : receiveMethod === 'onchain' ? 'Generate Address' : receiveMethod === 'lightning' ? 'Create Invoice' : 'Receive' }}
          </button>
        </div>
      </div>
    </div>

    <!-- Decentralized Web Node (DWN) -->
    <div class="glass-card p-6 mb-8">
      <div class="flex items-center justify-between mb-4">
        <div class="flex items-center gap-3">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
            </svg>
          </div>
          <div>
            <h2 class="text-lg font-semibold text-white">{{ t('web5.decentralizedWebNode') }}</h2>
            <p class="text-xs text-white/60">{{ t('web5.dwnDescription') }}</p>
          </div>
        </div>
        <router-link v-if="dwnInstalled && dwnRunning" to="/apps/dwn" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium inline-flex items-center gap-2 no-underline">
          {{ t('web5.manageDwn') }}
        </router-link>
      </div>

      <!-- DWN not installed or not running -->
      <div v-if="!dwnInstalled || !dwnRunning" class="py-6 text-center">
        <p class="text-white/60 text-sm mb-4">
          {{ !dwnInstalled ? 'The DWN container is not installed.' : 'The DWN container is not running.' }}
          {{ !dwnInstalled ? 'Install it from the App Store to enable decentralized data storage and sync.' : 'Start it from the App Store to enable decentralized data storage and sync.' }}
        </p>
        <router-link to="/dashboard/marketplace" class="glass-button px-4 py-2 rounded-lg text-sm font-medium inline-flex items-center gap-2 no-underline">
          <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="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 100 4 2 2 0 000-4z" />
          </svg>
          Open App Store
        </router-link>
      </div>

      <!-- Status (only shown when DWN is installed and running) -->
      <template v-if="dwnInstalled && dwnRunning">
      <div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">{{ t('common.status') }}</div>
          <div class="flex items-center gap-2">
            <div class="w-2 h-2 rounded-full" :class="dwnStatus?.running ? 'bg-green-400' : 'bg-red-400'"></div>
            <span class="text-sm text-white font-medium">{{ dwnStatus?.running ? t('common.running') : t('common.stopped') }}</span>
          </div>
        </div>
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Sync</div>
          <div class="flex items-center gap-2">
            <div class="w-2 h-2 rounded-full" :class="{
              'bg-green-400': dwnSyncStatus === 'synced',
              'bg-yellow-400 animate-pulse': dwnSyncStatus === 'syncing',
              'bg-red-400': dwnSyncStatus === 'error',
              'bg-white/30': dwnSyncStatus === 'idle'
            }"></div>
            <span class="text-sm text-white font-medium capitalize">{{ dwnSyncStatus }}</span>
          </div>
        </div>
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Storage</div>
          <span class="text-sm text-white font-medium">{{ formatDwnStorage }}</span>
        </div>
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Messages</div>
          <span class="text-sm text-white font-medium">{{ dwnStatus?.message_count ?? 0 }}</span>
        </div>
      </div>

      <!-- Protocols -->
      <div class="mb-4">
        <div class="flex items-center justify-between mb-2">
          <div class="text-xs text-white/50">Registered Protocols ({{ dwnProtocols.length }})</div>
          <button @click="showRegisterProtocol = !showRegisterProtocol" class="text-xs text-blue-400 hover:text-blue-300 transition-colors">
            {{ showRegisterProtocol ? 'Cancel' : '+ Register' }}
          </button>
        </div>
        <!-- Register Protocol Form -->
        <div v-if="showRegisterProtocol" class="bg-white/5 rounded-lg p-3 mb-3">
          <div class="flex gap-2 items-end">
            <div class="flex-1">
              <label class="text-xs text-white/50 block mb-1">Protocol URI</label>
              <input v-model="newProtocolUri" type="text" placeholder="https://example.com/protocol" class="w-full bg-black/30 border border-white/10 rounded-lg px-3 py-1.5 text-sm text-white placeholder-white/30 focus:outline-none focus:border-blue-500/50" />
            </div>
            <label class="flex items-center gap-1.5 text-xs text-white/60 cursor-pointer whitespace-nowrap pb-1.5">
              <input v-model="newProtocolPublished" type="checkbox" class="rounded bg-black/30 border-white/20" />
              Published
            </label>
            <button @click="registerDwnProtocol" :disabled="registeringProtocol || !newProtocolUri.trim()" class="glass-button glass-button-sm px-3 rounded-lg text-xs font-medium disabled:opacity-50 whitespace-nowrap">
              {{ registeringProtocol ? 'Registering...' : 'Register' }}
            </button>
          </div>
        </div>
        <div v-if="dwnProtocols.length" class="flex flex-wrap gap-2">
          <div v-for="proto in dwnProtocols" :key="proto.protocol" class="flex items-center gap-1.5 px-2 py-1 rounded-md bg-blue-500/15 border border-blue-500/20 text-xs text-blue-300 group">
            <span>{{ proto.protocol }}</span>
            <span v-if="proto.published" class="text-green-400/60" title="Published">&#x2022;</span>
            <button @click="removeDwnProtocol(proto.protocol)" :disabled="removingProtocol === proto.protocol" class="opacity-0 group-hover:opacity-100 text-red-400/60 hover:text-red-400 transition-all ml-1" title="Remove">
              &times;
            </button>
          </div>
        </div>
        <div v-else class="text-xs text-white/30 italic">No protocols registered</div>
      </div>

      <!-- Sync Targets -->
      <div v-if="dwnStatus?.peer_sync_targets?.length" class="mb-4">
        <div class="text-xs text-white/50 mb-2">Peer Sync Targets</div>
        <div class="space-y-1">
          <div v-for="target in dwnStatus.peer_sync_targets" :key="target" class="flex items-center gap-2 text-xs text-white/70 bg-white/5 rounded-lg px-3 py-2">
            <svg class="w-3 h-3 text-green-400 flex-shrink-0" fill="currentColor" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4" /></svg>
            <span class="truncate font-mono">{{ target }}</span>
          </div>
        </div>
      </div>

      <!-- Messages Browser -->
      <div class="mb-4">
        <div class="flex items-center justify-between mb-2">
          <div class="text-xs text-white/50">Messages</div>
          <button @click="toggleDwnMessages" class="text-xs text-blue-400 hover:text-blue-300 transition-colors">
            {{ showDwnMessages ? 'Hide' : 'Browse' }}
          </button>
        </div>
        <div v-if="showDwnMessages">
          <div v-if="loadingDwnMessages" class="text-xs text-white/40 py-4 text-center">Loading messages...</div>
          <div v-else-if="dwnMessages.length === 0" class="text-xs text-white/30 italic py-2">No messages stored</div>
          <div v-else class="space-y-2 max-h-64 overflow-y-auto">
            <div v-for="msg in dwnMessages" :key="msg.record_id" class="bg-white/5 rounded-lg p-3">
              <div class="flex items-center justify-between mb-1">
                <span class="text-xs font-mono text-white/50 truncate max-w-[200px]" :title="msg.record_id">{{ (msg.record_id || '').slice(0, 8) }}...</span>
                <span class="text-xs text-white/40">{{ new Date(msg.date_created).toLocaleString() }}</span>
              </div>
              <div class="flex flex-wrap gap-2 text-xs">
                <span class="text-white/70">{{ msg.author }}</span>
                <span v-if="msg.descriptor.protocol" class="text-blue-300/80">{{ msg.descriptor.protocol }}</span>
                <span v-if="msg.descriptor.schema" class="text-purple-300/80">{{ msg.descriptor.schema }}</span>
              </div>
              <div v-if="msg.data" class="mt-1 text-xs text-white/40 font-mono truncate">{{ JSON.stringify(msg.data).slice(0, 120) }}</div>
            </div>
          </div>
        </div>
      </div>

      <!-- Last Sync & Actions -->
      <div class="flex items-center justify-between pt-3 border-t border-white/10">
        <div class="text-xs text-white/40">
          {{ dwnStatus?.last_sync ? `Last sync: ${new Date(dwnStatus.last_sync).toLocaleString()}` : 'Never synced' }}
        </div>
        <button @click="syncDWNs" :disabled="syncingDWNs || !dwnStatus?.running" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium flex items-center gap-2 disabled:opacity-50">
          <svg class="w-4 h-4" :class="{ 'animate-spin': syncingDWNs }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
          </svg>
          {{ syncingDWNs ? t('web5.syncing') : t('web5.syncNow') }}
        </button>
      </div>
      </template>
    </div>

    <!-- Verifiable Credentials -->
    <div class="glass-card p-6 mb-8">
      <!-- Desktop: side-by-side -->
      <div class="hidden md:flex items-center justify-between mb-4">
        <div class="flex items-center gap-3">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
            </svg>
          </div>
          <div>
            <h2 class="text-lg font-semibold text-white">{{ t('web5.verifiableCredentials') }}</h2>
            <p class="text-xs text-white/60">{{ t('web5.verifiableCredentialsDesc') }}</p>
          </div>
        </div>
        <router-link to="/dashboard/web5/credentials" class="glass-button glass-button-sm px-3 rounded-lg text-sm font-medium flex items-center gap-2">
          Manage →
        </router-link>
      </div>
      <!-- Mobile: stacked -->
      <div class="md:hidden mb-4">
        <div class="flex items-center gap-3 mb-2">
          <div class="flex-shrink-0 w-10 h-10 rounded-lg bg-white/10 flex items-center justify-center">
            <svg class="w-5 h-5 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
            </svg>
          </div>
          <h2 class="text-lg font-semibold text-white">{{ t('web5.verifiableCredentials') }}</h2>
        </div>
        <p class="text-xs text-white/60 mb-3">{{ t('web5.verifiableCredentialsDesc') }}</p>
        <router-link to="/dashboard/web5/credentials" class="w-full min-h-[44px] glass-button rounded-lg text-sm font-medium flex items-center justify-center gap-2">
          Manage →
        </router-link>
      </div>

      <!-- Stats -->
      <div class="grid grid-cols-3 gap-3 mb-4">
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Total</div>
          <span class="text-sm text-white font-medium">{{ vcCredentials.length }}</span>
        </div>
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Active</div>
          <span class="text-sm text-green-400 font-medium">{{ vcCredentials.filter(c => c.status === 'active').length }}</span>
        </div>
        <div class="bg-white/5 rounded-lg p-3">
          <div class="text-xs text-white/50 mb-1">Identities</div>
          <span class="text-sm text-white font-medium">{{ managedIdentities.length }}</span>
        </div>
      </div>

      <!-- Credentials List (summary) -->
      <div v-if="vcCredentials.length" class="space-y-2">
        <div v-for="vc in vcCredentials.slice(0, 3)" :key="vc.id" class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
          <div class="min-w-0 flex-1">
            <div class="text-sm text-white font-medium">{{ vc.type }}</div>
            <div class="text-xs text-white/50 truncate">To: {{ (vc.subject || '').slice(0, 30) }}...</div>
          </div>
          <span :class="{
            'text-green-400': vc.status === 'active',
            'text-red-400': vc.status === 'revoked',
            'text-yellow-400': vc.status === 'expired'
          }" class="text-xs font-medium capitalize">{{ vc.status }}</span>
        </div>
        <router-link v-if="vcCredentials.length > 3" to="/dashboard/web5/credentials" class="block text-center text-xs text-white/50 hover:text-white/70 py-2 transition-colors">
          View all {{ vcCredentials.length }} credentials →
        </router-link>
      </div>
      <div v-else class="text-center text-white/40 text-sm py-4">
        {{ t('web5.noCredentials') }}
      </div>
    </div>

    <!-- Domains Management Modal -->
    <div v-if="showDomainsModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showDomainsModal = false" @keydown.escape="showDomainsModal = false">
      <div class="glass-card p-6 w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="domains-title">
        <div class="flex items-center justify-between mb-4">
          <h2 id="domains-title" class="text-lg font-bold text-white">{{ t('web5.domainsTitle') }}</h2>
          <button @click="showDomainsModal = false" class="text-white/40 hover:text-white/80 transition-colors">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
          </button>
        </div>

        <!-- Registered Names List -->
        <div v-if="registeredNames.length" class="space-y-2 mb-4">
          <div v-for="n in registeredNames" :key="n.id" class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div>
              <div class="text-sm text-white font-medium font-mono">{{ n.nip05 }}</div>
              <div class="text-xs text-white/50 truncate max-w-[200px]">DID: {{ n.did }}</div>
            </div>
            <div class="flex items-center gap-2">
              <span :class="{
                'text-green-400': n.status === 'active',
                'text-yellow-400': n.status === 'pending',
                'text-red-400': n.status === 'expired' || n.status === 'failed'
              }" class="text-xs font-medium capitalize">{{ n.status }}</span>
              <button @click="removeName(n.id)" class="text-white/30 hover:text-red-400 transition-colors p-1">
                <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="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
              </button>
            </div>
          </div>
        </div>
        <div v-else class="text-center text-white/40 text-sm py-4 mb-4">{{ t('web5.noDomains') }}</div>

        <!-- Register New Name -->
        <div class="border-t border-white/10 pt-4">
          <h3 class="text-sm font-semibold text-white mb-3">{{ t('web5.registerNewName') }}</h3>
          <div class="grid grid-cols-2 gap-3 mb-3">
            <div>
              <label class="text-white/60 text-xs block mb-1">Username</label>
              <input v-model="newDomainName" type="text" placeholder="satoshi" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            </div>
            <div>
              <label class="text-white/60 text-xs block mb-1">Domain</label>
              <input v-model="newDomainDomain" type="text" placeholder="example.com" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            </div>
          </div>
          <div class="mb-3">
            <label class="text-white/60 text-xs block mb-1">Link to Identity</label>
            <select v-model="newDomainIdentityId" class="w-full bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30">
              <option value="" disabled>Select identity...</option>
              <option v-for="id in managedIdentities" :key="id.id" :value="id.id">{{ id.name }} ({{ (id.did || '').slice(0, 24) }}...)</option>
            </select>
          </div>
          <div v-if="domainError" class="text-xs text-red-400 mb-2">{{ domainError }}</div>
          <button @click="registerNewName" :disabled="domainRegistering || !newDomainName.trim() || !newDomainDomain.trim() || !newDomainIdentityId" class="w-full glass-button px-4 py-2 rounded-lg text-sm font-medium disabled:opacity-50">
            {{ domainRegistering ? 'Registering...' : 'Register Name' }}
          </button>
        </div>

        <!-- Verify NIP-05 -->
        <div class="border-t border-white/10 pt-4 mt-4">
          <h3 class="text-sm font-semibold text-white mb-3">{{ t('web5.verifyNip05') }}</h3>
          <div class="flex gap-2">
            <input v-model="verifyNip05Input" type="text" placeholder="user@domain.com" class="flex-1 bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" />
            <button @click="verifyNip05" :disabled="nip05Verifying || !verifyNip05Input.trim()" class="glass-button px-4 py-2 rounded-lg text-sm font-medium disabled:opacity-50">
              {{ nip05Verifying ? '...' : 'Verify' }}
            </button>
          </div>
          <div v-if="nip05Result" class="mt-2 p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-2 mb-1">
              <div class="w-2 h-2 rounded-full" :class="nip05Result.verified ? 'bg-green-400' : 'bg-red-400'"></div>
              <span class="text-sm text-white font-medium">{{ nip05Result.verified ? 'Verified' : 'Not Found' }}</span>
            </div>
            <div v-if="nip05Result.nostr_pubkey" class="text-xs text-white/50 font-mono truncate">Pubkey: {{ nip05Result.nostr_pubkey }}</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Relay Management Modal -->
    <div v-if="showRelaysModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-md" @click.self="showRelaysModal = false" @keydown.escape="showRelaysModal = false">
      <div class="glass-card p-6 w-full max-w-lg mx-4 max-h-[80vh] overflow-y-auto" role="dialog" aria-modal="true" aria-labelledby="relays-title">
        <div class="flex items-center justify-between mb-4">
          <h2 id="relays-title" class="text-lg font-bold text-white">{{ t('web5.nostrRelays') }}</h2>
          <button @click="showRelaysModal = false" class="text-white/40 hover:text-white/80 transition-colors">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
          </button>
        </div>

        <!-- Relay List -->
        <div v-if="nostrRelays.length" class="space-y-2 mb-4">
          <div v-for="relay in nostrRelays" :key="relay.url" class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
            <div class="flex items-center gap-3 min-w-0 flex-1">
              <div class="w-2 h-2 rounded-full flex-shrink-0" :class="relay.connected ? 'bg-green-400' : 'bg-white/30'"></div>
              <span class="text-sm text-white font-mono truncate">{{ relay.url }}</span>
            </div>
            <div class="flex items-center gap-2 flex-shrink-0">
              <button @click="toggleNostrRelay(relay.url, !relay.enabled)" class="text-xs px-2 py-1 rounded" :class="relay.enabled ? 'bg-green-500/20 text-green-400' : 'bg-white/5 text-white/40'">
                {{ relay.enabled ? 'On' : 'Off' }}
              </button>
              <button @click="removeNostrRelay(relay.url)" class="text-white/30 hover:text-red-400 transition-colors p-1">
                <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="M6 18L18 6M6 6l12 12" /></svg>
              </button>
            </div>
          </div>
        </div>
        <div v-else class="text-center text-white/40 text-sm py-4 mb-4">{{ t('web5.noRelays') }}</div>

        <!-- Add Relay -->
        <div class="border-t border-white/10 pt-4">
          <h3 class="text-sm font-semibold text-white mb-3">{{ t('web5.addRelay') }}</h3>
          <div class="flex gap-2">
            <input v-model="newRelayUrl" type="text" :placeholder="t('web5.relayUrlPlaceholder')" class="flex-1 bg-white/5 border border-white/10 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-white/30" @keyup.enter="addNostrRelay" />
            <button @click="addNostrRelay" :disabled="!newRelayUrl.trim()" class="glass-button px-4 py-2 rounded-lg text-sm font-medium disabled:opacity-50">
              Add
            </button>
          </div>
          <div v-if="relayError" class="text-xs text-red-400 mt-2">{{ relayError }}</div>
        </div>
      </div>
    </div>

    <!-- Identity Toast -->
    <Transition name="content-fade">
      <div v-if="identityToastVisible" class="fixed bottom-24 md:bottom-8 left-1/2 -translate-x-1/2 z-50 px-4 py-2 rounded-lg bg-black/80 backdrop-blur-md border border-white/10 text-white text-sm shadow-lg">
        {{ identityToastText }}
      </div>
    </Transition>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { rpcClient } from '@/api/rpc-client'
import { useMessageToast } from '@/composables/useMessageToast'
import { useWeb5BadgeStore } from '@/stores/web5Badge'
import { useAppStore } from '@/stores/app'
import { PackageState } from '@/types/api'
import { useModalKeyboard } from '@/composables/useModalKeyboard'

const route = useRoute()
const router = useRouter()
const { t } = useI18n()
const messageToast = useMessageToast()
const web5Badge = useWeb5BadgeStore()
const appStore = useAppStore()

// --- Networking Profits ---
interface ProfitsData {
  total_sats: number
  content_sales_sats: number
  routing_fees_sats: number
}
const profitsBreakdown = ref<ProfitsData | null>(null)
const networkingProfitsDisplay = computed(() => {
  if (!profitsBreakdown.value) return '...'
  const sats = profitsBreakdown.value.total_sats
  if (sats === 0) return '0 sats'
  if (sats < 100000) return `${sats.toLocaleString()} sats`
  // Convert to BTC for large amounts
  const btc = sats / 100_000_000
  return `₿${btc.toFixed(8).replace(/0+$/, '').replace(/\.$/, '')}`
})

async function loadNetworkingProfits() {
  try {
    const res = await rpcClient.call<ProfitsData>({ method: 'wallet.networking-profits' })
    profitsBreakdown.value = res
  } catch {
    profitsBreakdown.value = { total_sats: 0, content_sales_sats: 0, routing_fees_sats: 0 }
  }
}

// --- Domain Names (NIP-05) ---
interface RegisteredNameData {
  id: string
  name: string
  domain: string
  nip05: string
  identity_id: string
  did: string
  nostr_pubkey: string | null
  status: string
  registered_at: string
  expires_at: string | null
}
interface Nip05Result {
  name: string
  domain: string
  nostr_pubkey: string | null
  relays: string[]
  verified: boolean
}
const registeredNames = ref<RegisteredNameData[]>([])
const showDomainsModal = ref(false)
const newDomainName = ref('')
const newDomainDomain = ref('')
const newDomainIdentityId = ref('')
const domainError = ref('')
const domainRegistering = ref(false)
const verifyNip05Input = ref('')
const nip05Verifying = ref(false)
const nip05Result = ref<Nip05Result | null>(null)

const activeNamesCount = computed(() => registeredNames.value.filter(n => n.status === 'active').length)
const expiringNamesCount = computed(() => registeredNames.value.filter(n => n.status === 'expired' || n.expires_at).length)

async function loadDomainNames() {
  try {
    const res = await rpcClient.call<{ names: RegisteredNameData[] }>({ method: 'identity.list-names' })
    registeredNames.value = res.names || []
  } catch {
    registeredNames.value = []
  }
}

async function registerNewName() {
  if (!newDomainName.value.trim() || !newDomainDomain.value.trim() || !newDomainIdentityId.value) return
  domainRegistering.value = true
  domainError.value = ''
  try {
    const identity = managedIdentities.value.find(i => i.id === newDomainIdentityId.value)
    await rpcClient.call({ method: 'identity.register-name', params: {
      name: newDomainName.value.trim(),
      domain: newDomainDomain.value.trim(),
      identity_id: newDomainIdentityId.value,
      did: identity?.did || '',
    }})
    newDomainName.value = ''
    newDomainDomain.value = ''
    newDomainIdentityId.value = ''
    await loadDomainNames()
  } catch (e: unknown) {
    domainError.value = e instanceof Error ? e.message : t('web5.registrationFailed')
  } finally {
    domainRegistering.value = false
  }
}

async function removeName(id: string) {
  try {
    await rpcClient.call({ method: 'identity.remove-name', params: { id } })
    await loadDomainNames()
  } catch (e: unknown) {
    domainError.value = e instanceof Error ? e.message : t('web5.removeFailed')
  }
}

async function verifyNip05() {
  if (!verifyNip05Input.value.trim()) return
  nip05Verifying.value = true
  nip05Result.value = null
  try {
    const res = await rpcClient.call<Nip05Result>({ method: 'identity.resolve-name', params: { identifier: verifyNip05Input.value.trim() } })
    nip05Result.value = res
  } catch {
    nip05Result.value = { name: '', domain: '', nostr_pubkey: null, relays: [], verified: false }
  } finally {
    nip05Verifying.value = false
  }
}

// --- Verifiable Credentials ---
interface VCData {
  id: string
  issuer: string
  subject: string
  type: string
  claims: Record<string, unknown>
  issued_at: string
  expires_at: string | null
  status: string
}
const vcCredentials = ref<VCData[]>([])

async function loadCredentials() {
  try {
    const res = await rpcClient.call<{ credentials: VCData[] }>({ method: 'identity.list-credentials' })
    vcCredentials.value = res.credentials || []
  } catch {
    vcCredentials.value = []
  }
}

// --- Nostr Relay Functions ---
async function loadNostrRelays() {
  try {
    const [relayRes, statsRes] = await Promise.all([
      rpcClient.call<{ relays: NostrRelayData[] }>({ method: 'nostr.list-relays' }),
      rpcClient.call<NostrRelayStatsData>({ method: 'nostr.get-stats' }),
    ])
    nostrRelays.value = relayRes.relays || []
    nostrRelayStats.value = statsRes
  } catch {
    nostrRelays.value = []
    nostrRelayStats.value = null
  }
}

async function addNostrRelay() {
  if (!newRelayUrl.value.trim()) return
  relayError.value = ''
  try {
    await rpcClient.call({ method: 'nostr.add-relay', params: { url: newRelayUrl.value.trim() } })
    newRelayUrl.value = ''
    await loadNostrRelays()
  } catch (e: unknown) {
    relayError.value = e instanceof Error ? e.message : t('web5.failedToAddRelay')
  }
}

async function removeNostrRelay(url: string) {
  try {
    await rpcClient.call({ method: 'nostr.remove-relay', params: { url } })
    await loadNostrRelays()
  } catch (e: unknown) {
    relayError.value = e instanceof Error ? e.message : t('web5.failedToRemoveRelay')
  }
}

async function toggleNostrRelay(url: string, enabled: boolean) {
  try {
    await rpcClient.call({ method: 'nostr.toggle-relay', params: { url, enabled } })
    await loadNostrRelays()
  } catch (e: unknown) {
    relayError.value = e instanceof Error ? e.message : t('web5.failedToToggleRelay')
  }
}

const storedDid = ref<string | null>(null)
try {
  storedDid.value = localStorage.getItem('neode_did') || null
} catch { /* noop */ }

const userDid = computed(() => storedDid.value)

const didStatus = computed<'active' | 'inactive' | 'pending'>(() =>
  userDid.value ? 'active' : 'inactive'
)

const creatingDid = ref(false)
const didCopied = ref(false)

async function createDID() {
  creatingDid.value = true
  try {
    // Try backend RPC first
    const res = await rpcClient.call<{ did: string }>({ method: 'identity.create-did' })
    storedDid.value = res.did
    localStorage.setItem('neode_did', res.did)
  } catch {
    // Fallback: generate a did:key locally using Web Crypto
    if (!crypto.subtle) {
      // crypto.subtle requires HTTPS — generate random fallback
      const randomBytes = new Uint8Array(32)
      crypto.getRandomValues(randomBytes)
      const hex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join('')
      const did = `did:key:z${hex}`
      storedDid.value = did
      localStorage.setItem('neode_did', did)
    } else {
      const keyPair = await crypto.subtle.generateKey(
        { name: 'ECDSA', namedCurve: 'P-256' },
        true,
        ['sign', 'verify']
      )
      const exported = await crypto.subtle.exportKey('raw', keyPair.publicKey)
      const bytes = new Uint8Array(exported)
      const hex = Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
      const did = `did:key:z${hex}`
      storedDid.value = did
      localStorage.setItem('neode_did', did)
    }
  } finally {
    creatingDid.value = false
  }
}

// did:dht state
const dhtDid = ref<string | null>(null)
const publishingDht = ref(false)
const dhtDidCopied = ref(false)

async function publishDhtDid() {
  publishingDht.value = true
  try {
    const identities = await rpcClient.call<{ identities: Array<{ id: string; is_default: boolean }> }>({ method: 'identity.list' })
    const defaultId = identities.identities?.find((i: { is_default: boolean }) => i.is_default)
    if (!defaultId) return
    const res = await rpcClient.call<{ dht_did: string }>({
      method: 'identity.create-dht-did',
      params: { identity_id: defaultId.id }
    })
    dhtDid.value = res.dht_did
    localStorage.setItem('neode_dht_did', res.dht_did)
  } catch (e) {
    console.error('DHT publish failed:', e)
  } finally {
    publishingDht.value = false
  }
}

async function refreshDhtDid() {
  publishingDht.value = true
  try {
    const identities = await rpcClient.call<{ identities: Array<{ id: string; is_default: boolean }> }>({ method: 'identity.list' })
    const defaultId = identities.identities?.find((i: { is_default: boolean }) => i.is_default)
    if (!defaultId) return
    await rpcClient.call({ method: 'identity.refresh-dht-did', params: { identity_id: defaultId.id } })
  } catch (e) {
    console.error('DHT refresh failed:', e)
  } finally {
    publishingDht.value = false
  }
}

async function copyDhtDid() {
  if (!dhtDid.value) return
  await safeClipboardWrite(dhtDid.value)
  dhtDidCopied.value = true
  setTimeout(() => { dhtDidCopied.value = false }, 2000)
}

// Load saved dht_did on mount
try {
  dhtDid.value = localStorage.getItem('neode_dht_did') || null
} catch { /* noop */ }

async function copyDid() {
  if (!userDid.value) return
  await safeClipboardWrite(userDid.value)
  didCopied.value = true
  setTimeout(() => { didCopied.value = false }, 2000)
}

// DID Document modal
const showDidDocModal = ref(false)
const loadingDidDoc = ref(false)
const didDocumentData = ref<Record<string, unknown> | null>(null)
const didDocVerified = ref<boolean | null>(null)
const didDocCopied = ref(false)

const didDocumentFormatted = computed(() =>
  didDocumentData.value ? JSON.stringify(didDocumentData.value, null, 2) : ''
)

async function showDidDocument() {
  showDidDocModal.value = true
  loadingDidDoc.value = true
  didDocVerified.value = null
  try {
    const doc = await rpcClient.resolveDid()
    didDocumentData.value = doc
    // Verify the document
    const verification = await rpcClient.call({
      method: 'identity.verify-did-document',
      params: { document: doc },
    }) as { valid: boolean }
    didDocVerified.value = verification.valid
  } catch (err) {
    if (import.meta.env.DEV) console.error('Failed to load DID Document:', err)
    didDocumentData.value = null
  } finally {
    loadingDidDoc.value = false
  }
}

async function copyDidDocument() {
  if (!didDocumentFormatted.value) return
  await safeClipboardWrite(didDocumentFormatted.value)
  didDocCopied.value = true
  setTimeout(() => { didDocCopied.value = false }, 2000)
}

// DWN Status & Sync
interface DwnStatusData {
  running: boolean
  version: string
  sync_status: string
  last_sync: string | null
  messages_synced: number
  storage_bytes: number
  message_count: number
  protocol_count: number
  registered_protocols: string[]
  peer_sync_targets: string[]
}
interface DwnProtocol {
  protocol: string
  published: boolean
  types: Record<string, unknown>
  structure: Record<string, unknown>
  dateRegistered: string
}
interface DwnMessageEntry {
  record_id: string
  author: string
  date_created: string
  descriptor: {
    interface: string
    method: string
    protocol?: string
    schema?: string
    dataFormat?: string
  }
  data?: unknown
}
const dwnStatus = ref<DwnStatusData | null>(null)
const dwnSyncStatus = ref<'synced' | 'syncing' | 'error' | 'idle'>('idle')
const dwnInstalled = computed(() => !!appStore.packages['dwn'])
const dwnRunning = computed(() => appStore.packages['dwn']?.state === PackageState.Running)
const syncingDWNs = ref(false)
const dwnProtocols = ref<DwnProtocol[]>([])
const dwnMessages = ref<DwnMessageEntry[]>([])
const showDwnMessages = ref(false)
const loadingDwnMessages = ref(false)
const showRegisterProtocol = ref(false)
const newProtocolUri = ref('')
const newProtocolPublished = ref(false)
const registeringProtocol = ref(false)
const removingProtocol = ref<string | null>(null)

const formatDwnStorage = computed(() => {
  if (!dwnStatus.value) return '0 B'
  const bytes = dwnStatus.value.storage_bytes
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`
})

async function loadDwnStatus() {
  try {
    const res = await rpcClient.call<DwnStatusData>({ method: 'dwn.status' })
    dwnStatus.value = res
    dwnSyncStatus.value = (res.sync_status as 'synced' | 'syncing' | 'error' | 'idle') || 'idle'
  } catch {
    dwnStatus.value = null
    dwnSyncStatus.value = 'idle'
  }
}

// Wallet Connection & LND Balances
const walletConnected = ref(false)
const connectingWallet = ref(false)
const lndOnchainBalance = ref(0)
const lndChannelBalance = ref(0)

// Nostr Relays
interface NostrRelayData {
  url: string
  connected: boolean
  enabled: boolean
  added_at: string
}
interface NostrRelayStatsData {
  total_relays: number
  connected_count: number
  enabled_count: number
}
const nostrRelays = ref<NostrRelayData[]>([])
const nostrRelayStats = ref<NostrRelayStatsData | null>(null)
const showRelaysModal = ref(false)
const newRelayUrl = ref('')
const relayError = ref('')

// Connected Nodes (peers)
const peers = ref<Array<{ onion: string; pubkey: string; name?: string }>>([])
const loadingPeers = ref(false)
const peerReachableLocal = ref<Record<string, boolean>>({})
const peerReachable = computed(() => ({ ...appStore.peerHealth, ...peerReachableLocal.value }))
const connectedNodesCount = computed(() => peers.value.length)

// Hardware wallet detection
interface HwWalletDevice {
  type: string
  vendor_id: string
  product_id: string
  manufacturer: string
  product: string
}
const detectedHwWallets = ref<HwWalletDevice[]>([])

async function detectHardwareWallets() {
  try {
    const res = await rpcClient.detectUsbDevices()
    detectedHwWallets.value = res.devices || []
  } catch {
    detectedHwWallets.value = []
  }
}

// Send Message modal
const showSendMessageModal = ref(false)
const sendMessageModalRef = ref<HTMLElement | null>(null)
const sendMessageRestoreFocusRef = ref<HTMLElement | null>(null)
function closeSendMessageModal() {
  sendMessageRestoreFocusRef.value?.focus?.()
  showSendMessageModal.value = false
}
useModalKeyboard(sendMessageModalRef, showSendMessageModal, closeSendMessageModal, { restoreFocusRef: sendMessageRestoreFocusRef })
const sendMessageTo = ref('')
const sendMessageText = ref('')
const sendingMessage = ref(false)
const sendMessageError = ref('')
const sendMessageSuccess = ref('')
const discovering = ref(false)

// Connected Nodes container: tabs + messages (uses shared composable for polling from Dashboard)
const nodesContainerRef = ref<HTMLElement | null>(null)
const nodesContainerTab = ref<'peers' | 'messages' | 'requests'>('peers')
const { receivedMessages, loadingMessages, unreadCount, loadReceivedMessages, markAsRead } = messageToast

function formatMessageTime(ts: string): string {
  try {
    const d = new Date(ts)
    const now = new Date()
    const diff = now.getTime() - d.getTime()
    if (diff < 60000) return 'Just now'
    if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`
    if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`
    return d.toLocaleDateString()
  } catch {
    return ts
  }
}

function switchToMessagesTab() {
  nodesContainerTab.value = 'messages'
  markAsRead()
}

async function loadPeers() {
  loadingPeers.value = true
  try {
    const res = await rpcClient.listPeers()
    const peerList = res.peers || []

    // Also load federated nodes and merge them into the peers list
    try {
      const fedRes = await rpcClient.federationListNodes()
      const fedNodes = fedRes.nodes || []
      for (const n of fedNodes) {
        if (n.onion && !peerList.some(p => p.onion === n.onion || p.pubkey === n.pubkey)) {
          peerList.push({ onion: n.onion, pubkey: n.pubkey, name: n.name || `Federation: ${n.did?.slice(0, 16) || 'node'}` })
        }
      }
    } catch {
      // Federation may not be set up — ignore
    }

    peers.value = peerList
    for (const p of peers.value) {
      try {
        const check = await rpcClient.checkPeerReachable(p.onion)
        peerReachableLocal.value[p.onion] = check.reachable
      } catch {
        peerReachableLocal.value[p.onion] = false
      }
    }
  } catch (e) {
    if (import.meta.env.DEV) console.error('Failed to load peers:', e)
  } finally {
    loadingPeers.value = false
  }
}

async function sendMessage() {
  if (!sendMessageTo.value || !sendMessageText.value.trim()) return
  sendingMessage.value = true
  sendMessageError.value = ''
  sendMessageSuccess.value = ''
  try {
    await rpcClient.sendMessageToPeer(sendMessageTo.value, sendMessageText.value.trim())
    sendMessageSuccess.value = t('web5.messageSent')
    sendMessageText.value = ''
    setTimeout(() => {
      showSendMessageModal.value = false
      sendMessageSuccess.value = ''
    }, 1500)
  } catch (e) {
    sendMessageError.value = e instanceof Error ? e.message : t('web5.failedToSend')
  } finally {
    sendingMessage.value = false
  }
}

async function discoverAndAddPeers() {
  discovering.value = true
  try {
    const res = await rpcClient.discoverNodes()
    const nodes = res.nodes || []
    for (const n of nodes) {
      if (n.onion && n.pubkey) {
        try {
          await rpcClient.addPeer({ onion: n.onion, pubkey: n.pubkey })
        } catch (e) {
          if (import.meta.env.DEV) console.warn('Peer may already exist', e)
        }
      }
    }
    await loadPeers()
  } catch (e) {
    if (import.meta.env.DEV) console.error('Discover failed:', e)
  } finally {
    discovering.value = false
  }
}

// --- Wallet (Unified Send/Receive) ---
const ecashBalance = ref(0)
const ecashTokenCount = ref(0)
const ecashTxCount = ref(0)
const ecashSendToken = ref('')
const ecashReceiveToken = ref('')
const ecashReceiveResult = ref('')

// Unified Send
const showUnifiedSendModal = ref(false)
const sendMethod = ref<'auto' | 'lightning' | 'onchain' | 'ecash'>('auto')
const unifiedSendAmount = ref<number>(0)
const unifiedSendDest = ref('')
const unifiedSendProcessing = ref(false)
const unifiedSendError = ref('')
const sendResultTxid = ref('')
const sendResultHash = ref('')
const useHardwareWallet = ref(false)
const psbtData = ref('')
const psbtStep = ref<'idle' | 'created' | 'finalizing'>('idle')
const signedPsbtInput = ref('')

// Unified Receive
const showUnifiedReceiveModal = ref(false)
const receiveMethod = ref<'lightning' | 'onchain' | 'ecash'>('lightning')
const receiveInvoiceAmount = ref<number>(0)
const receiveInvoiceMemo = ref('')
const receiveInvoiceResult = ref('')
const receiveOnchainAddress = ref('')
const unifiedReceiveProcessing = ref(false)
const unifiedReceiveError = ref('')

const effectiveSendMethod = computed(() => {
  if (sendMethod.value !== 'auto') return sendMethod.value
  const amt = unifiedSendAmount.value || 0
  if (amt <= 0) return 'lightning'
  if (amt < 1000) return 'ecash'
  if (amt > 500000) return 'onchain'
  return 'lightning'
})

async function loadEcashBalance() {
  try {
    const res = await rpcClient.call<{ balance_sats: number; token_count: number }>({ method: 'wallet.ecash-balance' })
    ecashBalance.value = res.balance_sats ?? 0
    ecashTokenCount.value = res.token_count ?? 0
  } catch {
    ecashBalance.value = 0
    ecashTokenCount.value = 0
  }
  try {
    const hist = await rpcClient.call<{ transactions: unknown[] }>({ method: 'wallet.ecash-history' })
    ecashTxCount.value = hist.transactions?.length ?? 0
  } catch {
    ecashTxCount.value = 0
  }
}

function closeUnifiedSendModal() {
  showUnifiedSendModal.value = false
  ecashSendToken.value = ''
  unifiedSendError.value = ''
  sendResultTxid.value = ''
  sendResultHash.value = ''
  psbtData.value = ''
  psbtStep.value = 'idle'
  signedPsbtInput.value = ''
}

function closeUnifiedReceiveModal() {
  showUnifiedReceiveModal.value = false
  receiveInvoiceResult.value = ''
  receiveOnchainAddress.value = ''
  ecashReceiveToken.value = ''
  ecashReceiveResult.value = ''
  unifiedReceiveError.value = ''
}

async function unifiedSend() {
  if (!unifiedSendAmount.value || unifiedSendProcessing.value) return
  unifiedSendProcessing.value = true
  unifiedSendError.value = ''
  ecashSendToken.value = ''
  sendResultTxid.value = ''
  sendResultHash.value = ''

  const method = effectiveSendMethod.value
  try {
    if (method === 'ecash') {
      const res = await rpcClient.call<{ token: string }>({
        method: 'wallet.ecash-send',
        params: { amount_sats: unifiedSendAmount.value },
      })
      ecashSendToken.value = res.token
    } else if (method === 'lightning') {
      if (!unifiedSendDest.value.trim()) {
        unifiedSendError.value = t('web5.pasteInvoice')
        return
      }
      const res = await rpcClient.call<{ payment_hash: string; amount_sats: number }>({
        method: 'lnd.payinvoice',
        params: { payment_request: unifiedSendDest.value.trim() },
      })
      sendResultHash.value = res.payment_hash
    } else {
      if (!unifiedSendDest.value.trim()) {
        unifiedSendError.value = t('web5.enterBitcoinAddress')
        return
      }
      if (useHardwareWallet.value) {
        // Hardware wallet flow: create unsigned PSBT
        const res = await rpcClient.createPsbt({
          outputs: [{ address: unifiedSendDest.value.trim(), amount_sats: unifiedSendAmount.value }],
        })
        psbtData.value = res.psbt_base64
        psbtStep.value = 'created'
        signedPsbtInput.value = ''
        unifiedSendProcessing.value = false
        return
      }
      const res = await rpcClient.call<{ txid: string }>({
        method: 'lnd.sendcoins',
        params: { addr: unifiedSendDest.value.trim(), amount: unifiedSendAmount.value },
      })
      sendResultTxid.value = res.txid
    }
    await loadEcashBalance()
    await loadLndBalances()
  } catch (err: unknown) {
    unifiedSendError.value = err instanceof Error ? err.message : t('web5.sendFailed')
  } finally {
    unifiedSendProcessing.value = false
  }
}

async function finalizePsbt() {
  if (!signedPsbtInput.value.trim() || unifiedSendProcessing.value) return
  unifiedSendProcessing.value = true
  unifiedSendError.value = ''
  try {
    await rpcClient.finalizePsbt(signedPsbtInput.value.trim())
    psbtStep.value = 'idle'
    psbtData.value = ''
    signedPsbtInput.value = ''
    sendResultTxid.value = t('web5.broadcastViaHwWallet')
    await loadLndBalances()
  } catch (err: unknown) {
    unifiedSendError.value = err instanceof Error ? err.message : t('web5.broadcastFailed')
  } finally {
    unifiedSendProcessing.value = false
  }
}

function copyPsbt() {
  if (!psbtData.value) return
  window.safeClipboardWrite(psbtData.value)
  unifiedSendError.value = t('web5.psbtCopied')
}

function downloadPsbt() {
  if (!psbtData.value) return
  const blob = new Blob([psbtData.value], { type: 'text/plain' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = 'transaction.psbt'
  a.click()
  URL.revokeObjectURL(url)
}

function handlePsbtFileUpload(event: Event) {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return
  const reader = new FileReader()
  reader.onload = (e) => {
    signedPsbtInput.value = (e.target?.result as string) || ''
  }
  reader.readAsText(file)
  input.value = ''
}

async function unifiedReceive() {
  if (unifiedReceiveProcessing.value) return
  unifiedReceiveProcessing.value = true
  unifiedReceiveError.value = ''

  try {
    if (receiveMethod.value === 'lightning') {
      if (!receiveInvoiceAmount.value || receiveInvoiceAmount.value < 1) {
        unifiedReceiveError.value = t('web5.enterAmount')
        return
      }
      const res = await rpcClient.call<{ payment_request: string }>({
        method: 'lnd.createinvoice',
        params: { amount_sats: receiveInvoiceAmount.value, memo: receiveInvoiceMemo.value },
      })
      receiveInvoiceResult.value = res.payment_request
    } else if (receiveMethod.value === 'onchain') {
      const res = await rpcClient.call<{ address: string }>({ method: 'lnd.newaddress' })
      receiveOnchainAddress.value = res.address
    } else {
      if (!ecashReceiveToken.value.trim()) {
        unifiedReceiveError.value = t('web5.pasteEcashToken')
        return
      }
      const res = await rpcClient.call<{ received_sats: number }>({
        method: 'wallet.ecash-receive',
        params: { token: ecashReceiveToken.value.trim() },
      })
      ecashReceiveResult.value = `Received ${res.received_sats} sats!`
      ecashReceiveToken.value = ''
      await loadEcashBalance()
    }
  } catch (err: unknown) {
    unifiedReceiveError.value = err instanceof Error ? err.message : t('web5.receiveFailed')
  } finally {
    unifiedReceiveProcessing.value = false
  }
}

function copyEcashToken(token: string) {
  safeClipboardWrite(token)
  showIdentityToast(t('web5.ecashTokenCopied'))
}

async function safeClipboardWrite(text: string): Promise<void> {
  if (navigator.clipboard?.writeText) {
    await safeClipboardWrite(text)
  } else {
    const ta = document.createElement('textarea')
    ta.value = text
    ta.style.position = 'fixed'
    ta.style.opacity = '0'
    document.body.appendChild(ta)
    ta.select()
    document.execCommand('copy')
    document.body.removeChild(ta)
  }
}

function copyToClipboard(text: string, msg: string) {
  safeClipboardWrite(text)
  showIdentityToast(msg)
}

// --- Shared Content ---
interface ContentItemData {
  id: string
  filename: string
  mime_type: string
  size_bytes: number
  description: string
  access: string | { paid: { price_sats: number } }
  added_at: string
}

const contentItems = ref<ContentItemData[]>([])
const contentLoading = ref(false)
const showAddContentModal = ref(false)
const newContentFilename = ref('')
const newContentMimeType = ref('application/octet-stream')
const newContentDescription = ref('')
const newContentAccess = ref<'free' | 'peers_only' | 'paid'>('free')
const newContentPrice = ref<number>(100)
const addingContent = ref(false)
const addContentError = ref('')
const removingContentId = ref<string | null>(null)
const updatingPricingId = ref<string | null>(null)

const accessOptions = [
  { value: 'free' as const, label: 'Free' },
  { value: 'peers_only' as const, label: 'Peers Only' },
  { value: 'paid' as const, label: 'Paid' },
]

function getAccessType(item: ContentItemData): 'free' | 'peers_only' | 'paid' {
  if (typeof item.access === 'string') {
    if (item.access === 'peersonly' || item.access === 'peers_only') return 'peers_only'
    if (item.access === 'paid') return 'paid'
    return 'free'
  }
  if (item.access && typeof item.access === 'object' && 'paid' in item.access) return 'paid'
  return 'free'
}

function getItemPrice(item: ContentItemData): number {
  if (typeof item.access === 'object' && item.access && 'paid' in item.access) {
    return item.access.paid.price_sats
  }
  return 0
}

function formatBytes(bytes: number): string {
  if (bytes === 0) return '0 B'
  const units = ['B', 'KB', 'MB', 'GB']
  const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1)
  return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`
}

async function loadContentItems() {
  contentLoading.value = true
  try {
    const res = await rpcClient.call<{ items: ContentItemData[] }>({ method: 'content.list-mine' })
    contentItems.value = res.items || []
  } catch {
    contentItems.value = []
  } finally {
    contentLoading.value = false
  }
}

async function addContentItem() {
  if (addingContent.value || !newContentFilename.value.trim()) return
  addingContent.value = true
  addContentError.value = ''
  try {
    await rpcClient.call({
      method: 'content.add',
      params: {
        filename: newContentFilename.value.trim(),
        mime_type: newContentMimeType.value.trim() || 'application/octet-stream',
        description: newContentDescription.value.trim(),
      },
    })
    // If paid access was selected, set pricing after adding
    if (newContentAccess.value !== 'free') {
      const items = (await rpcClient.call<{ items: ContentItemData[] }>({ method: 'content.list-mine' })).items || []
      const latest = items[items.length - 1]
      if (latest) {
        await rpcClient.call({
          method: 'content.set-pricing',
          params: {
            id: latest.id,
            access: newContentAccess.value,
            ...(newContentAccess.value === 'paid' ? { price_sats: newContentPrice.value || 100 } : {}),
          },
        })
      }
    }
    showAddContentModal.value = false
    newContentFilename.value = ''
    newContentMimeType.value = 'application/octet-stream'
    newContentDescription.value = ''
    newContentAccess.value = 'free'
    newContentPrice.value = 100
    await loadContentItems()
    showIdentityToast(t('web5.contentAdded'))
  } catch (err: unknown) {
    addContentError.value = err instanceof Error ? err.message : t('web5.failedToAddContent')
  } finally {
    addingContent.value = false
  }
}

async function removeContentItem(id: string) {
  removingContentId.value = id
  try {
    await rpcClient.call({ method: 'content.remove', params: { id } })
    contentItems.value = contentItems.value.filter(i => i.id !== id)
    showIdentityToast(t('web5.contentRemoved'))
  } catch {
    showIdentityToast(t('web5.failedToRemoveContent'))
  } finally {
    removingContentId.value = null
  }
}

async function setContentPricing(item: ContentItemData, access: 'free' | 'peers_only' | 'paid') {
  updatingPricingId.value = item.id
  try {
    const params: Record<string, unknown> = { id: item.id, access }
    if (access === 'paid') {
      params.price_sats = getItemPrice(item) || 100
    }
    await rpcClient.call({ method: 'content.set-pricing', params })
    await loadContentItems()
  } catch {
    showIdentityToast(t('web5.failedToUpdatePricing'))
  } finally {
    updatingPricingId.value = null
  }
}

async function updateItemPrice(item: ContentItemData, value: string) {
  const price = parseInt(value, 10)
  if (!price || price <= 0) return
  updatingPricingId.value = item.id
  try {
    await rpcClient.call({
      method: 'content.set-pricing',
      params: { id: item.id, access: 'paid', price_sats: price },
    })
    await loadContentItems()
  } catch {
    showIdentityToast(t('web5.failedToUpdatePrice'))
  } finally {
    updatingPricingId.value = null
  }
}

// --- Content Tab + Browse Peers ---
const contentTab = ref<'mine' | 'browse'>('mine')
const browsePeerOnion = ref('')
const browsingPeerContent = ref(false)
const browsePeerError = ref('')
interface PeerContentItem {
  id: string
  filename: string
  mime_type: string
  size_bytes: number
  description: string
  access: string | { paid: { price_sats: number } }
}
const peerContentItems = ref<PeerContentItem[]>([])

function isMediaType(mime: string): boolean {
  return mime.startsWith('audio/') || mime.startsWith('video/')
}

function getPeerItemPrice(item: PeerContentItem): number {
  if (typeof item.access === 'object' && item.access && 'paid' in item.access) {
    return item.access.paid.price_sats
  }
  return 0
}

async function browsePeerContent() {
  if (!browsePeerOnion.value || browsingPeerContent.value) return
  browsingPeerContent.value = true
  browsePeerError.value = ''
  peerContentItems.value = []
  try {
    const res = await rpcClient.call<{ items: PeerContentItem[] }>({
      method: 'content.browse-peer',
      params: { onion: browsePeerOnion.value },
    })
    peerContentItems.value = res.items || []
    if (peerContentItems.value.length === 0) {
      browsePeerError.value = ''
    }
  } catch (err: unknown) {
    browsePeerError.value = err instanceof Error ? err.message : t('web5.failedToConnectPeer')
  } finally {
    browsingPeerContent.value = false
  }
}

// --- Content Streaming Player ---
const streamingItem = ref<PeerContentItem | null>(null)
const streamUrl = ref('')
const streamCostSats = ref(0)
const streamProgress = ref(0)
const playerError = ref('')
const audioPlayerRef = ref<HTMLAudioElement | null>(null)
const videoPlayerRef = ref<HTMLVideoElement | null>(null)

function streamPeerContent(item: PeerContentItem) {
  if (!browsePeerOnion.value) return
  streamingItem.value = item
  streamUrl.value = `http://${browsePeerOnion.value}/content/${item.id}`
  streamCostSats.value = getPeerItemPrice(item)
  streamProgress.value = 0
  playerError.value = ''
}

function downloadPeerContent(item: PeerContentItem) {
  if (!browsePeerOnion.value) return
  const url = `http://${browsePeerOnion.value}/content/${item.id}`
  showIdentityToast(t('web5.downloadUrlCopied'))
  safeClipboardWrite(url)
}

function closePlayer() {
  // Stop playback
  if (audioPlayerRef.value) {
    audioPlayerRef.value.pause()
    audioPlayerRef.value.src = ''
  }
  if (videoPlayerRef.value) {
    videoPlayerRef.value.pause()
    videoPlayerRef.value.src = ''
  }
  streamingItem.value = null
  streamUrl.value = ''
  streamProgress.value = 0
  playerError.value = ''
}

function onPlayerTimeUpdate() {
  const player = audioPlayerRef.value || videoPlayerRef.value
  if (player && player.duration > 0) {
    streamProgress.value = player.currentTime / player.duration
  }
}

function onPlayerError() {
  playerError.value = t('web5.playerError')
}

function copyStreamUrl() {
  if (streamUrl.value) {
    safeClipboardWrite(streamUrl.value)
    showIdentityToast(t('web5.streamUrlCopied'))
  }
}

// --- Connection Requests ---
interface ConnectionRequest {
  id: string
  from_did: string
  from_onion?: string
  from_pubkey?: string
  message?: string
  created_at: string
}

const connectionRequests = ref<ConnectionRequest[]>([])
const loadingRequests = ref(false)
const processingRequestId = ref<string | null>(null)

async function loadConnectionRequests() {
  loadingRequests.value = true
  try {
    const res = await rpcClient.call<{ requests: ConnectionRequest[] }>({ method: 'network.list-requests' })
    connectionRequests.value = res.requests || []
    web5Badge.pendingRequestCount = connectionRequests.value.length
  } catch {
    connectionRequests.value = []
  } finally {
    loadingRequests.value = false
  }
}

function switchToRequestsTab() {
  nodesContainerTab.value = 'requests'
  if (connectionRequests.value.length === 0 && !loadingRequests.value) {
    loadConnectionRequests()
  }
}

async function acceptRequest(requestId: string) {
  processingRequestId.value = requestId
  try {
    await rpcClient.call({ method: 'network.accept-request', params: { request_id: requestId } })
    connectionRequests.value = connectionRequests.value.filter(r => r.id !== requestId)
    web5Badge.pendingRequestCount = connectionRequests.value.length
    await loadPeers()
    showIdentityToast(t('web5.connectionAccepted'))
  } catch {
    showIdentityToast(t('web5.failedToAcceptRequest'))
  } finally {
    processingRequestId.value = null
  }
}

async function rejectRequest(requestId: string) {
  processingRequestId.value = requestId
  try {
    await rpcClient.call({ method: 'network.reject-request', params: { request_id: requestId } })
    connectionRequests.value = connectionRequests.value.filter(r => r.id !== requestId)
    web5Badge.pendingRequestCount = connectionRequests.value.length
    showIdentityToast(t('web5.requestRejected'))
  } catch {
    showIdentityToast(t('web5.failedToRejectRequest'))
  } finally {
    processingRequestId.value = null
  }
}

const identityToastText = ref('')
const identityToastVisible = ref(false)
let identityToastTimer: ReturnType<typeof setTimeout> | undefined

function showIdentityToast(text: string) {
  identityToastText.value = text
  identityToastVisible.value = true
  clearTimeout(identityToastTimer)
  identityToastTimer = setTimeout(() => { identityToastVisible.value = false }, 2000)
}

// --- Node Visibility ---
type VisibilityLevel = 'hidden' | 'discoverable' | 'public'

const nodeVisibility = ref<VisibilityLevel>('hidden')
const nodeOnionAddress = ref<string | null>(null)
const visibilityLoading = ref(false)
const settingVisibility = ref(false)

const visibilityOptions = [
  { value: 'hidden' as VisibilityLevel, label: 'Hidden', description: 'Your node is not discoverable by others' },
  { value: 'discoverable' as VisibilityLevel, label: 'Discoverable', description: 'Federated peers can find and connect to your node' },
  { value: 'public' as VisibilityLevel, label: 'Public', description: 'Accepting connections from any Archipelago node' },
]

async function loadVisibility() {
  visibilityLoading.value = true
  try {
    const res = await rpcClient.call<{ visibility: string; onion_address?: string }>({ method: 'network.get-visibility' })
    nodeVisibility.value = (res.visibility as VisibilityLevel) || 'hidden'
    nodeOnionAddress.value = res.onion_address || null
  } catch {
    nodeVisibility.value = 'hidden'
  } finally {
    visibilityLoading.value = false
  }
}

async function setVisibility(level: VisibilityLevel) {
  if (settingVisibility.value || nodeVisibility.value === level) return
  settingVisibility.value = true
  try {
    const res = await rpcClient.call<{ visibility: string; onion_address?: string }>({
      method: 'network.set-visibility',
      params: { visibility: level },
    })
    nodeVisibility.value = (res.visibility as VisibilityLevel) || level
    nodeOnionAddress.value = res.onion_address || nodeOnionAddress.value
    showIdentityToast(t('web5.visibilitySetTo', { level }))
  } catch {
    showIdentityToast(t('web5.failedToUpdateVisibility'))
  } finally {
    settingVisibility.value = false
  }
}

function copyOnionAddress() {
  if (!nodeOnionAddress.value) return
  safeClipboardWrite(nodeOnionAddress.value)
  showIdentityToast(t('web5.onionAddressCopied'))
}

// --- Identity Management ---
interface ManagedIdentity {
  id: string
  name: string
  purpose: string
  pubkey: string
  did: string
  created_at: string
  is_default: boolean
}

const managedIdentities = ref<ManagedIdentity[]>([])
const identitiesLoading = ref(false)
const showCreateIdentityModal = ref(false)
const newIdentityName = ref('Personal')
const newIdentityPurpose = ref('personal')
const creatingIdentity = ref(false)
const createIdentityError = ref<string | null>(null)
const deleteIdentityTarget = ref<ManagedIdentity | null>(null)
const deletingIdentity = ref(false)

async function loadIdentities() {
  identitiesLoading.value = true
  try {
    const res = await rpcClient.call<{ identities: ManagedIdentity[] }>({ method: 'identity.list' })
    managedIdentities.value = res.identities || []
  } catch {
    managedIdentities.value = []
  } finally {
    identitiesLoading.value = false
  }
}

async function createIdentity() {
  if (creatingIdentity.value) return
  createIdentityError.value = null
  creatingIdentity.value = true
  try {
    await rpcClient.call({
      method: 'identity.create',
      params: { name: newIdentityName.value.trim() || 'Personal', purpose: newIdentityPurpose.value },
    })
    showCreateIdentityModal.value = false
    newIdentityName.value = 'Personal'
    newIdentityPurpose.value = 'personal'
    await loadIdentities()
    showIdentityToast(t('web5.identityCreated'))
  } catch (err: unknown) {
    createIdentityError.value = err instanceof Error ? err.message : t('web5.failedToCreateIdentity')
  } finally {
    creatingIdentity.value = false
  }
}

function copyIdentityDid(did: string) {
  safeClipboardWrite(did)
  showIdentityToast(t('web5.didCopied'))
}

async function setDefaultIdentity(id: string) {
  try {
    await rpcClient.call({ method: 'identity.set-default', params: { id } })
    await loadIdentities()
    showIdentityToast(t('web5.defaultIdentityUpdated'))
  } catch {
    showIdentityToast(t('web5.failedToSetDefault'))
  }
}

function confirmDeleteIdentity(identity: ManagedIdentity) {
  deleteIdentityTarget.value = identity
}

async function deleteIdentity() {
  if (!deleteIdentityTarget.value || deletingIdentity.value) return
  deletingIdentity.value = true
  try {
    await rpcClient.call({ method: 'identity.delete', params: { id: deleteIdentityTarget.value.id } })
    deleteIdentityTarget.value = null
    await loadIdentities()
    showIdentityToast(t('web5.identityDeleted'))
  } catch {
    showIdentityToast(t('web5.failedToDeleteIdentity'))
  } finally {
    deletingIdentity.value = false
  }
}

onMounted(() => {
  loadPeers()
  loadReceivedMessages()
  loadIdentities()
  loadVisibility()
  loadConnectionRequests()
  loadEcashBalance()
  loadContentItems()
  loadNetworkingProfits()
  loadDwnStatus()
  loadDwnProtocols()
  loadDomainNames()
  loadNostrRelays()
  loadCredentials()
  loadLndBalances()
  detectHardwareWallets()
  // Open Messages tab when navigated via toast (e.g. ?tab=messages)
  if (route.query.tab === 'messages') {
    nodesContainerTab.value = 'messages'
    markAsRead()
    nextTick(() => {
      nodesContainerRef.value?.scrollIntoView({ behavior: 'smooth', block: 'center' })
    })
  }
})

watch(() => route.query.tab, (tab) => {
  if (tab === 'messages') {
    nodesContainerTab.value = 'messages'
    markAsRead()
    nextTick(() => {
      nodesContainerRef.value?.scrollIntoView({ behavior: 'smooth', block: 'center' })
    })
  }
})


async function syncDWNs() {
  syncingDWNs.value = true
  dwnSyncStatus.value = 'syncing'
  try {
    const res = await rpcClient.call<{ sync_status: string; last_sync: string; messages_synced: number }>({ method: 'dwn.sync' })
    dwnSyncStatus.value = (res.sync_status as 'synced' | 'syncing' | 'error' | 'idle') || 'synced'
    await loadDwnStatus()
  } catch {
    dwnSyncStatus.value = 'error'
  } finally {
    syncingDWNs.value = false
  }
}

async function loadDwnProtocols() {
  try {
    const res = await rpcClient.call<{ protocols: DwnProtocol[] }>({ method: 'dwn.list-protocols' })
    dwnProtocols.value = res.protocols || []
  } catch {
    dwnProtocols.value = []
  }
}

async function registerDwnProtocol() {
  if (registeringProtocol.value || !newProtocolUri.value.trim()) return
  registeringProtocol.value = true
  try {
    await rpcClient.call({ method: 'dwn.register-protocol', params: { protocol: newProtocolUri.value.trim(), published: newProtocolPublished.value } })
    newProtocolUri.value = ''
    newProtocolPublished.value = false
    showRegisterProtocol.value = false
    await loadDwnProtocols()
    await loadDwnStatus()
  } catch {
    if (import.meta.env.DEV) console.error('Failed to register protocol')
  } finally {
    registeringProtocol.value = false
  }
}

async function removeDwnProtocol(protocol: string) {
  removingProtocol.value = protocol
  try {
    await rpcClient.call({ method: 'dwn.remove-protocol', params: { protocol } })
    await loadDwnProtocols()
    await loadDwnStatus()
  } catch {
    if (import.meta.env.DEV) console.error('Failed to remove protocol')
  } finally {
    removingProtocol.value = null
  }
}

async function toggleDwnMessages() {
  showDwnMessages.value = !showDwnMessages.value
  if (showDwnMessages.value) {
    await loadDwnMessages()
  }
}

async function loadDwnMessages() {
  loadingDwnMessages.value = true
  try {
    const res = await rpcClient.call<{ messages: DwnMessageEntry[]; count: number }>({ method: 'dwn.query-messages', params: { limit: 50 } })
    dwnMessages.value = res.messages || []
  } catch {
    dwnMessages.value = []
  } finally {
    loadingDwnMessages.value = false
  }
}

async function loadLndBalances() {
  try {
    const res = await rpcClient.call<{
      balance_sats: number
      channel_balance_sats: number
      synced_to_chain: boolean
    }>({ method: 'lnd.getinfo' })
    lndOnchainBalance.value = res.balance_sats || 0
    lndChannelBalance.value = res.channel_balance_sats || 0
    walletConnected.value = true
  } catch {
    walletConnected.value = false
    lndOnchainBalance.value = 0
    lndChannelBalance.value = 0
  }
}

async function connectWallet() {
  if (walletConnected.value) {
    walletConnected.value = false
  } else {
    connectingWallet.value = true
    await loadLndBalances()
    connectingWallet.value = false
  }
}

function manageRelays() {
  showRelaysModal.value = true
}
</script>


