<template>
  <div>
    <div class="hidden md:block mb-8">
      <h1 class="text-3xl font-bold text-white mb-2">Federation</h1>
      <p class="text-white/70">Manage trusted node clusters and sync state across your network</p>
      <p class="text-sm text-white/60 mt-2">{{ nodes.length }} federated node{{ nodes.length !== 1 ? 's' : '' }}</p>
    </div>

    <!-- Quick Actions -->
    <div class="glass-card p-6 mb-6">
      <div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
        <!-- Generate Invite -->
        <div data-controller-container tabindex="0" class="flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0">
          <div class="flex items-center gap-3 min-w-0">
            <svg class="w-5 h-5 text-orange-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
            </svg>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">Invite Node</p>
              <p class="text-xs text-white/60">Generate invite code</p>
            </div>
          </div>
          <button
            @click="generateInvite"
            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="generatingInvite"
          >
            {{ generatingInvite ? 'Generating...' : 'Generate' }}
          </button>
        </div>

        <!-- Join Federation -->
        <div data-controller-container tabindex="0" class="flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0">
          <div class="flex items-center gap-3 min-w-0">
            <svg class="w-5 h-5 text-blue-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
            </svg>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">Join</p>
              <p class="text-xs text-white/60">Accept an invite code</p>
            </div>
          </div>
          <button
            @click="showJoinModal = true"
            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"
          >
            Join
          </button>
        </div>

        <!-- Sync State -->
        <div data-controller-container tabindex="0" class="flex flex-col gap-3 p-4 bg-white/5 rounded-lg min-w-0">
          <div class="flex items-center gap-3 min-w-0">
            <svg class="w-5 h-5 text-green-400 shrink-0" 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>
            <div class="min-w-0">
              <p class="text-sm font-medium text-white">Sync</p>
              <p class="text-xs text-white/60">Refresh all node states</p>
            </div>
          </div>
          <button
            @click="syncAll"
            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="syncing"
          >
            {{ syncing ? 'Syncing...' : 'Sync Now' }}
          </button>
        </div>
      </div>
    </div>

    <!-- Invite Code Display -->
    <div v-if="inviteCode" class="glass-card p-6 mb-6">
      <div class="flex items-center justify-between mb-4">
        <h2 class="text-lg font-semibold text-white">Invite Code</h2>
        <button @click="inviteCode = ''" class="text-white/40 hover:text-white/70 transition-colors text-sm">Dismiss</button>
      </div>
      <p class="text-xs text-white/60 mb-3">Share this code with the node you want to federate with. It can only be used once.</p>
      <div class="bg-black/30 rounded-lg p-4 font-mono text-xs text-orange-300 break-all select-all">{{ inviteCode }}</div>
      <button
        @click="copyInviteCode"
        class="mt-3 px-4 py-2 glass-button rounded text-sm text-white/90 hover:text-white transition-colors"
      >
        {{ copiedInvite ? 'Copied' : 'Copy to Clipboard' }}
      </button>
    </div>

    <!-- Sync Results -->
    <div v-if="syncResults.length > 0" class="glass-card p-6 mb-6">
      <div class="flex items-center justify-between mb-4">
        <h2 class="text-lg font-semibold text-white">Sync Results</h2>
        <button @click="syncResults = []" class="text-white/40 hover:text-white/70 transition-colors text-sm">Dismiss</button>
      </div>
      <div class="space-y-2">
        <div v-for="r in syncResults" :key="r.did" class="flex items-center gap-3 p-3 bg-white/5 rounded-lg">
          <div class="w-2 h-2 rounded-full shrink-0" :class="r.status === 'ok' ? 'bg-green-400' : 'bg-red-400'"></div>
          <span class="text-sm text-white/80 font-mono truncate">{{ shortDid(r.did) }}</span>
          <span v-if="r.status === 'ok'" class="text-xs text-green-400">{{ r.apps }} apps</span>
          <span v-else class="text-xs text-red-400 truncate">{{ r.error }}</span>
        </div>
      </div>
    </div>

    <!-- Error Display -->
    <div v-if="error" class="glass-card p-4 mb-6 border-red-400/30">
      <p class="text-sm text-red-400">{{ error }}</p>
    </div>

    <!-- Federated Nodes List -->
    <div class="glass-card p-6 mb-6">
      <h2 class="text-lg font-semibold text-white mb-4">Federated Nodes</h2>

      <div v-if="loading" class="flex items-center gap-3 py-8 justify-center">
        <div class="w-5 h-5 border-2 border-white/20 border-t-orange-400 rounded-full animate-spin"></div>
        <span class="text-white/60 text-sm">Loading nodes...</span>
      </div>

      <div v-else-if="nodes.length === 0" class="text-center py-12">
        <svg class="w-16 h-16 text-white/20 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
          <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
        </svg>
        <p class="text-white/50 text-sm mb-2">No federated nodes yet</p>
        <p class="text-white/30 text-xs">Generate an invite code or join an existing federation</p>
      </div>

      <div v-else class="space-y-3">
        <div
          v-for="node in nodes"
          :key="node.did"
          class="bg-black/20 rounded-xl border border-white/10 p-4 cursor-pointer hover:border-white/20 transition-colors"
          @click="selectedNode = node"
        >
          <div class="flex items-center justify-between mb-2">
            <div class="flex items-center gap-3 min-w-0">
              <div class="w-2.5 h-2.5 rounded-full shrink-0" :class="isOnline(node) ? 'bg-green-400' : 'bg-white/30'"></div>
              <span class="text-sm font-medium text-white truncate">{{ node.name || shortDid(node.did) }}</span>
            </div>
            <span
              class="text-xs px-2 py-0.5 rounded-full shrink-0"
              :class="trustBadgeClass(node.trust_level)"
            >{{ node.trust_level }}</span>
          </div>

          <div class="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs text-white/50">
            <div>
              <span class="text-white/30">Apps:</span>
              {{ node.last_state?.apps?.length ?? '--' }}
            </div>
            <div>
              <span class="text-white/30">CPU:</span>
              {{ node.last_state?.cpu_usage_percent != null ? node.last_state.cpu_usage_percent.toFixed(1) + '%' : '--' }}
            </div>
            <div class="flex items-center gap-1">
              <span class="text-white/30">DWN:</span>
              <span class="w-1.5 h-1.5 rounded-full" :class="dwnSyncDotClass"></span>
            </div>
            <div>
              <span class="text-white/30">Seen:</span>
              {{ node.last_seen ? timeAgo(node.last_seen) : 'never' }}
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Node Detail Modal -->
    <div v-if="selectedNode" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm" @click.self="selectedNode = null; confirmRemove = false">
      <div class="glass-card p-6 w-full max-w-lg max-h-[80vh] overflow-y-auto">
        <div class="flex items-center justify-between mb-6">
          <h2 class="text-xl font-semibold text-white">Node Details</h2>
          <button @click="selectedNode = null; confirmRemove = false" class="text-white/40 hover:text-white/70 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 class="space-y-4">
          <div class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-1">DID</p>
            <p class="text-sm text-white/80 font-mono break-all">{{ selectedNode.did }}</p>
          </div>
          <div class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-1">Onion Address</p>
            <p class="text-sm text-white/80 font-mono break-all">{{ selectedNode.onion }}</p>
          </div>
          <div class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-1">Trust Level</p>
            <div class="flex items-center gap-2 mt-1">
              <select
                :value="selectedNode.trust_level"
                @change="changeTrust(selectedNode.did, ($event.target as HTMLSelectElement).value)"
                class="bg-black/30 text-white text-sm rounded px-2 py-1 border border-white/10"
              >
                <option value="trusted">Trusted</option>
                <option value="observer">Observer</option>
                <option value="untrusted">Untrusted</option>
              </select>
            </div>
          </div>
          <div class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-1">Added</p>
            <p class="text-sm text-white/80">{{ selectedNode.added_at }}</p>
          </div>

          <div v-if="selectedNode.last_state" class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-2">Resource Usage</p>
            <div class="grid grid-cols-2 gap-2 text-sm text-white/70">
              <div>CPU: {{ selectedNode.last_state.cpu_usage_percent?.toFixed(1) ?? '--' }}%</div>
              <div>Uptime: {{ selectedNode.last_state.uptime_secs ? formatUptime(selectedNode.last_state.uptime_secs) : '--' }}</div>
              <div>RAM: {{ formatBytes(selectedNode.last_state.mem_used_bytes) }} / {{ formatBytes(selectedNode.last_state.mem_total_bytes) }}</div>
              <div>Disk: {{ formatBytes(selectedNode.last_state.disk_used_bytes) }} / {{ formatBytes(selectedNode.last_state.disk_total_bytes) }}</div>
            </div>
          </div>

          <div v-if="selectedNode.last_state?.apps?.length" class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-2">Apps ({{ selectedNode.last_state.apps.length }})</p>
            <div class="space-y-1">
              <div v-for="app in selectedNode.last_state.apps" :key="app.id" class="flex items-center justify-between text-sm">
                <span class="text-white/80">{{ app.id }}</span>
                <span class="text-xs" :class="app.status === 'running' ? 'text-green-400' : 'text-white/40'">{{ app.status }}</span>
              </div>
            </div>
          </div>

          <!-- Deploy App (trusted only) -->
          <div v-if="selectedNode.trust_level === 'trusted'" class="bg-white/5 rounded-lg p-3">
            <p class="text-xs text-white/40 mb-2">Deploy App</p>
            <div class="flex gap-2">
              <input
                v-model="deployAppId"
                placeholder="App ID (e.g. bitcoin)"
                class="flex-1 bg-black/30 text-white text-sm rounded px-2 py-1.5 border border-white/10 focus:border-orange-400/50 focus:outline-none"
              />
              <button
                @click="deployApp(selectedNode.did)"
                class="px-3 py-1.5 glass-button rounded text-xs text-white/90 font-medium disabled:opacity-50"
                :disabled="deploying || !deployAppId.trim()"
              >
                {{ deploying ? 'Deploying...' : 'Deploy' }}
              </button>
            </div>
            <p v-if="deployResult" class="text-xs mt-2" :class="deployResult.startsWith('Error') ? 'text-red-400' : 'text-green-400'">{{ deployResult }}</p>
          </div>

          <!-- DWN Sync -->
          <div class="bg-white/5 rounded-lg p-3">
            <div class="flex items-center justify-between mb-2">
              <p class="text-xs text-white/40">DWN Sync</p>
              <div class="flex items-center gap-1.5">
                <span class="w-1.5 h-1.5 rounded-full" :class="dwnSyncDotClass"></span>
                <span class="text-xs text-white/50">{{ dwnSyncLabel }}</span>
              </div>
            </div>
            <div class="grid grid-cols-2 gap-2 text-sm text-white/70 mb-3">
              <div><span class="text-white/30">Messages:</span> {{ dwnStatus?.message_count ?? '--' }}</div>
              <div><span class="text-white/30">Last sync:</span> {{ dwnStatus?.last_sync ? timeAgo(dwnStatus.last_sync) : 'never' }}</div>
            </div>
            <button
              @click="triggerDwnSync"
              class="px-3 py-1.5 glass-button rounded text-xs text-white/90 font-medium disabled:opacity-50"
              :disabled="dwnSyncing"
            >
              {{ dwnSyncing ? 'Syncing...' : 'Sync Now' }}
            </button>
          </div>

          <div v-if="!confirmRemove">
            <button
              @click="confirmRemove = true"
              class="w-full mt-4 px-4 py-2 rounded text-sm text-red-400 border border-red-400/30 hover:bg-red-400/10 transition-colors"
            >
              Remove from Federation
            </button>
          </div>
          <div v-else class="mt-4 p-3 bg-red-400/10 rounded-lg border border-red-400/20">
            <p class="text-sm text-red-400 mb-3">Are you sure? This node will be removed from your federation.</p>
            <div class="flex gap-3">
              <button
                @click="confirmRemove = false"
                class="flex-1 px-3 py-1.5 glass-button rounded text-sm text-white/70"
              >Cancel</button>
              <button
                @click="removeNode(selectedNode!.did)"
                class="flex-1 px-3 py-1.5 rounded text-sm text-red-400 border border-red-400/30 hover:bg-red-400/10 transition-colors font-medium"
              >Confirm Remove</button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Join Modal -->
    <div v-if="showJoinModal" class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm" @click.self="showJoinModal = false">
      <div class="glass-card p-6 w-full max-w-md">
        <div class="flex items-center justify-between mb-6">
          <h2 class="text-xl font-semibold text-white">Join Federation</h2>
          <button @click="showJoinModal = false" class="text-white/40 hover:text-white/70 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>

        <p class="text-sm text-white/60 mb-4">Paste the invite code from the node you want to federate with.</p>

        <textarea
          v-model="joinCode"
          placeholder="fed1:..."
          rows="3"
          class="w-full bg-black/30 text-white text-sm rounded-lg p-3 border border-white/10 focus:border-orange-400/50 focus:outline-none font-mono resize-none"
        ></textarea>

        <div v-if="joinError" class="mt-3 text-sm text-red-400">{{ joinError }}</div>
        <div v-if="joinSuccess" class="mt-3 text-sm text-green-400">Successfully joined federation</div>

        <div class="flex gap-3 mt-4">
          <button
            @click="showJoinModal = false"
            class="flex-1 px-4 py-2 glass-button rounded text-sm text-white/70"
          >Cancel</button>
          <button
            @click="joinFederation"
            class="flex-1 px-4 py-2 glass-button rounded text-sm text-white font-medium disabled:opacity-50"
            :disabled="joining || !joinCode.trim()"
          >
            {{ joining ? 'Joining...' : 'Join' }}
          </button>
        </div>
      </div>
    </div>

  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { rpcClient } from '@/api/rpc-client'

interface AppStatus {
  id: string
  status: string
  version?: string
}

interface NodeState {
  timestamp: string
  apps: AppStatus[]
  cpu_usage_percent?: number
  mem_used_bytes?: number
  mem_total_bytes?: number
  disk_used_bytes?: number
  disk_total_bytes?: number
  uptime_secs?: number
  tor_active?: boolean
}

interface FederatedNode {
  did: string
  pubkey: string
  onion: string
  trust_level: string
  added_at: string
  name?: string
  last_seen?: string
  last_state?: NodeState
}

const nodes = ref<FederatedNode[]>([])
const loading = ref(true)
const error = ref('')
const selectedNode = ref<FederatedNode | null>(null)

const inviteCode = ref('')
const generatingInvite = ref(false)
const copiedInvite = ref(false)

const showJoinModal = ref(false)
const joinCode = ref('')
const joining = ref(false)
const joinError = ref('')
const joinSuccess = ref(false)

const syncing = ref(false)
const syncResults = ref<Array<{ did: string; status: string; apps?: number; error?: string }>>([])

const confirmRemove = ref(false)

const deployAppId = ref('')
const deploying = ref(false)
const deployResult = ref('')

interface DwnStatus {
  sync_status: string
  last_sync: string | null
  messages_synced: number
  message_count: number
}

const dwnStatus = ref<DwnStatus | null>(null)
const dwnSyncing = ref(false)

const dwnSyncDotClass = computed(() => {
  if (!dwnStatus.value) return 'bg-white/30'
  switch (dwnStatus.value.sync_status) {
    case 'synced': return 'bg-green-400'
    case 'syncing': return 'bg-yellow-400 animate-pulse'
    case 'error': return 'bg-red-400'
    default: return 'bg-white/30'
  }
})

const dwnSyncLabel = computed(() => {
  if (!dwnStatus.value) return 'Unknown'
  switch (dwnStatus.value.sync_status) {
    case 'synced': return 'Synced'
    case 'syncing': return 'Syncing...'
    case 'error': return 'Error'
    default: return dwnStatus.value.sync_status
  }
})

async function loadNodes() {
  try {
    loading.value = true
    const result = await rpcClient.federationListNodes()
    nodes.value = result.nodes
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Failed to load nodes'
  } finally {
    loading.value = false
  }
}

async function generateInvite() {
  try {
    generatingInvite.value = true
    error.value = ''
    const result = await rpcClient.federationInvite()
    inviteCode.value = result.code
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Failed to generate invite'
  } finally {
    generatingInvite.value = false
  }
}

async function copyInviteCode() {
  try {
    await window.navigator.clipboard.writeText(inviteCode.value)
  } catch {
    const ta = document.createElement('textarea')
    ta.value = inviteCode.value
    ta.style.position = 'fixed'
    ta.style.opacity = '0'
    document.body.appendChild(ta)
    ta.select()
    document.execCommand('copy')
    document.body.removeChild(ta)
  }
  copiedInvite.value = true
  setTimeout(() => { copiedInvite.value = false }, 2000)
}

async function joinFederation() {
  try {
    joining.value = true
    joinError.value = ''
    joinSuccess.value = false
    await rpcClient.federationJoin(joinCode.value.trim())
    joinSuccess.value = true
    joinCode.value = ''
    await loadNodes()
    setTimeout(() => { showJoinModal.value = false; joinSuccess.value = false }, 1500)
  } catch (e) {
    joinError.value = e instanceof Error ? e.message : 'Failed to join'
  } finally {
    joining.value = false
  }
}

async function syncAll() {
  try {
    syncing.value = true
    error.value = ''
    const result = await rpcClient.federationSyncState()
    syncResults.value = result.results
    await loadNodes()
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Sync failed'
  } finally {
    syncing.value = false
  }
}

async function changeTrust(did: string, level: string) {
  try {
    await rpcClient.federationSetTrust(did, level as 'trusted' | 'observer' | 'untrusted')
    await loadNodes()
    if (selectedNode.value?.did === did) {
      selectedNode.value = nodes.value.find(n => n.did === did) ?? null
    }
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Failed to update trust level'
  }
}

async function removeNode(did: string) {
  try {
    await rpcClient.federationRemoveNode(did)
    confirmRemove.value = false
    selectedNode.value = null
    await loadNodes()
  } catch (e) {
    confirmRemove.value = false
    error.value = e instanceof Error ? e.message : 'Failed to remove node'
  }
}

async function deployApp(did: string) {
  try {
    deploying.value = true
    deployResult.value = ''
    await rpcClient.federationDeployApp({
      did,
      appId: deployAppId.value.trim(),
    })
    deployResult.value = `Successfully deployed ${deployAppId.value} to remote node`
    deployAppId.value = ''
  } catch (e) {
    deployResult.value = `Error: ${e instanceof Error ? e.message : 'Deploy failed'}`
  } finally {
    deploying.value = false
  }
}

async function loadDwnStatus() {
  try {
    const result = await rpcClient.call<DwnStatus>({ method: 'dwn.status' })
    dwnStatus.value = result
  } catch {
    dwnStatus.value = null
  }
}

async function triggerDwnSync() {
  try {
    dwnSyncing.value = true
    await rpcClient.call({ method: 'dwn.sync', timeout: 120000 })
    await loadDwnStatus()
  } catch {
    // Silently handle sync errors
  } finally {
    dwnSyncing.value = false
  }
}

function isOnline(node: FederatedNode): boolean {
  if (!node.last_seen) return false
  const lastSeen = new Date(node.last_seen).getTime()
  const tenMinutesAgo = Date.now() - 10 * 60 * 1000
  return lastSeen > tenMinutesAgo
}

function shortDid(did: string): string {
  if (did.length <= 24) return did
  return did.slice(0, 16) + '...' + did.slice(-8)
}

function timeAgo(iso: string): string {
  const seconds = Math.floor((Date.now() - new Date(iso).getTime()) / 1000)
  if (seconds < 60) return 'just now'
  if (seconds < 3600) return Math.floor(seconds / 60) + 'm ago'
  if (seconds < 86400) return Math.floor(seconds / 3600) + 'h ago'
  return Math.floor(seconds / 86400) + 'd ago'
}

function formatBytes(bytes?: number): string {
  if (bytes == null || bytes === 0) return '--'
  const units = ['B', 'KB', 'MB', 'GB', 'TB']
  let i = 0
  let val = bytes
  while (val >= 1024 && i < units.length - 1) {
    val /= 1024
    i++
  }
  return val.toFixed(1) + ' ' + units[i]
}

function formatUptime(secs: number): string {
  const days = Math.floor(secs / 86400)
  const hours = Math.floor((secs % 86400) / 3600)
  if (days > 0) return `${days}d ${hours}h`
  const mins = Math.floor((secs % 3600) / 60)
  return `${hours}h ${mins}m`
}

function trustBadgeClass(level: string): string {
  switch (level) {
    case 'trusted': return 'bg-green-400/20 text-green-400'
    case 'observer': return 'bg-blue-400/20 text-blue-400'
    case 'untrusted': return 'bg-white/10 text-white/50'
    default: return 'bg-white/10 text-white/50'
  }
}

onMounted(() => {
  loadNodes()
  loadDwnStatus()
})
</script>
