<script setup lang="ts">
/**
 * FleetView — Read-only fleet overview: hosts and game instances for this license.
 *
 * Data flow: useFleetStore → GET /api/fleet → tenant-scoped AgentHost + GameInstance rows.
 *
 * Render states:
 *   - loading  → shows skeleton / loading text
 *   - error    → shows error panel (fetch failed / 401 → error state, NOT global error boundary)
 *   - empty    → honest empty state with CTA to /server
 *   - populated → summary strip + one card per host + instance list under each
 *
 * No fabricated data. All nulls render as '—' via safeFixed/safeDate.
 */
import { onMounted, computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useFleetStore } from '@/stores/fleet'
import { useToastStore } from '@/stores/toast'
import type { FleetHost } from '@/stores/fleet'
import { safeFixed, safeDate } from '@/utils/formatters'
import Panel from '@/components/ds/data/Panel.vue'
import StatCard from '@/components/ds/data/StatCard.vue'
import Badge from '@/components/ds/core/Badge.vue'
import StatusDot from '@/components/ds/core/StatusDot.vue'
import Button from '@/components/ds/core/Button.vue'
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
import Icon from '@/components/ds/core/Icon.vue'

// ---------------------------------------------------------------------------
// Store / router
// ---------------------------------------------------------------------------
const fleet = useFleetStore()
const router = useRouter()
const toast = useToastStore()

onMounted(() => {
  fleet.fetchFleet()
})

// ---------------------------------------------------------------------------
// Derived state
// ---------------------------------------------------------------------------
const hasHosts = computed(() => fleet.hosts.length > 0)

// ---------------------------------------------------------------------------
// Remove host (offline only — a live agent re-registers)
// ---------------------------------------------------------------------------
const confirmHostId = ref<string | null>(null)
const removingHostId = ref<string | null>(null)

async function removeHost(host: FleetHost) {
  removingHostId.value = host.id
  try {
    await fleet.removeHost(host.id)
    toast.success(`Removed ${host.hostname}`)
  } catch (e) {
    toast.error(e instanceof Error ? e.message : 'Failed to remove host')
  } finally {
    removingHostId.value = null
    confirmHostId.value = null
  }
}

/** Map host status → Badge tone */
function hostTone(status: string): 'online' | 'offline' | 'warn' {
  if (status === 'connected') return 'online'
  if (status === 'degraded') return 'warn'
  return 'offline'
}

function hostStatusLabel(status: string): string {
  if (status === 'connected') return 'Online'
  if (status === 'degraded') return 'Degraded'
  return 'Offline'
}

/** Map game instance state → Badge tone */
function instanceTone(state: string): 'online' | 'offline' | 'warn' | 'neutral' {
  if (state === 'running') return 'online'
  if (state === 'crashed') return 'offline'
  if (state === 'stopped') return 'warn'
  return 'neutral'
}

/** Format uptime seconds → human-readable "Xd Xh Xm" */
function formatUptime(seconds: number | null): string {
  if (seconds == null || seconds < 0) return '—'
  const d = Math.floor(seconds / 86400)
  const h = Math.floor((seconds % 86400) / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  if (d > 0) return `${d}d ${h}h`
  if (h > 0) return `${h}h ${m}m`
  return `${m}m`
}

/** Format memory used/total as "Xm / Xm" or "—" if null. */
function formatMem(used: number | null, total: number | null): string {
  if (used == null && total == null) return '—'
  const u = used != null ? `${Math.round(used)}MB` : '—'
  const t = total != null ? `${Math.round(total)}MB` : '—'
  return `${u} / ${t}`
}

/** Pick primary disk (first entry) for display. */
function primaryDisk(host: FleetHost): string {
  if (!host.disks || host.disks.length === 0) return '—'
  const d = host.disks[0]
  if (d == null) return '—'
  const freePct = d.total_mb > 0 ? Math.round((d.free_mb / d.total_mb) * 100) : 0
  return `${d.mount} · ${freePct}% free`
}

/** Last heartbeat relative time — use safeDate, then strip full timestamp for brevity. */
function relativeHeartbeat(iso: string | null): string {
  if (!iso) return 'Never'
  return safeDate(iso)
}
</script>

<template>
  <div class="fleet-view">
    <!-- Page header -->
    <div class="fleet-view__header">
      <div>
        <h1 class="fleet-view__title">Fleet</h1>
        <p class="fleet-view__sub">Hosts and game instances connected to this license.</p>
      </div>
      <Button variant="ghost" icon="refresh-cw" :disabled="fleet.loading" @click="fleet.fetchFleet()">
        Refresh
      </Button>
    </div>

    <!-- Loading state -->
    <div v-if="fleet.loading && !hasHosts" class="fleet-view__loading">
      <Icon name="loader" :size="18" class="fleet-loading-icon" />
      <span>Loading fleet data…</span>
    </div>

    <!-- Error state (API failed / 401 / network error) — honest, not global error boundary -->
    <Panel v-else-if="fleet.error && !hasHosts" title="Could not load fleet data">
      <EmptyState
        icon="wifi-off"
        title="Fleet data unavailable"
        :description="fleet.error"
      >
        <template #action>
          <Button variant="primary" @click="fleet.fetchFleet()">Try again</Button>
        </template>
      </EmptyState>
    </Panel>

    <!-- Empty state — no hosts returned -->
    <Panel v-else-if="!fleet.loading && !fleet.error && !hasHosts">
      <EmptyState
        icon="server"
        title="No hosts connected yet"
        description="Install re-Agent on your server machine to see it here."
      >
        <template #action>
          <Button variant="primary" @click="router.push('/server')">Go to Server page</Button>
        </template>
      </EmptyState>
    </Panel>

    <!-- Populated fleet -->
    <template v-else>
      <!-- Summary strip -->
      <div class="fleet-view__summary">
        <StatCard
          label="Total hosts"
          :value="fleet.summary.host_count"
          icon="server"
        />
        <StatCard
          label="Online hosts"
          :value="fleet.summary.online_host_count"
          icon="activity"
        />
        <StatCard
          label="Game instances"
          :value="fleet.summary.instance_count"
          icon="layers"
        />
      </div>

      <!-- Host cards -->
      <div class="fleet-view__hosts">
        <Panel
          v-for="host in fleet.hosts"
          :key="host.id"
          class="fleet-host"
        >
          <!-- Host header -->
          <template #default>
            <div class="fleet-host__head">
              <div class="fleet-host__identity">
                <StatusDot :tone="hostTone(host.status)" :pulse="host.status === 'connected'" :size="9" />
                <span class="fleet-host__name">{{ host.hostname }}</span>
                <Badge :tone="hostTone(host.status)" :dot="false">{{ hostStatusLabel(host.status) }}</Badge>
              </div>
              <div class="fleet-host__meta">
                <span class="fleet-host__meta-item" v-if="host.agent_version">
                  <Icon name="zap" :size="12" />v{{ host.agent_version }}
                </span>
                <span class="fleet-host__meta-item" v-if="host.os || host.arch">
                  <Icon name="cpu" :size="12" />{{ [host.os, host.arch].filter(Boolean).join(' / ') }}
                </span>
                <!-- Remove host — offline only; a live agent re-registers -->
                <template v-if="confirmHostId === host.id">
                  <span class="fleet-host__confirm">Remove host &amp; its instances?</span>
                  <Button
                    variant="danger-soft"
                    size="sm"
                    :loading="removingHostId === host.id"
                    @click="removeHost(host)"
                  >Remove</Button>
                  <Button variant="ghost" size="sm" :disabled="removingHostId === host.id" @click="confirmHostId = null">Cancel</Button>
                </template>
                <Button
                  v-else-if="host.status !== 'connected'"
                  variant="ghost"
                  size="sm"
                  icon="trash-2"
                  @click="confirmHostId = host.id"
                >Remove</Button>
              </div>
            </div>

            <!-- Host metrics row -->
            <div class="fleet-host__metrics">
              <div class="fleet-metric">
                <span class="fleet-metric__label">CPU</span>
                <span class="fleet-metric__value">
                  {{ host.cpu_percent != null ? safeFixed(host.cpu_percent, 1) + '%' : '—' }}
                  <span v-if="host.cpu_cores" class="fleet-metric__sub">{{ host.cpu_cores }} cores</span>
                </span>
              </div>
              <div class="fleet-metric">
                <span class="fleet-metric__label">Memory</span>
                <span class="fleet-metric__value">{{ formatMem(host.mem_used_mb, host.mem_total_mb) }}</span>
              </div>
              <div class="fleet-metric">
                <span class="fleet-metric__label">Disk</span>
                <span class="fleet-metric__value">{{ primaryDisk(host) }}</span>
              </div>
              <div class="fleet-metric">
                <span class="fleet-metric__label">Uptime</span>
                <span class="fleet-metric__value">{{ formatUptime(host.uptime_seconds) }}</span>
              </div>
              <div class="fleet-metric">
                <span class="fleet-metric__label">Last heartbeat</span>
                <span class="fleet-metric__value fleet-metric__value--sm">{{ relativeHeartbeat(host.last_heartbeat_at) }}</span>
              </div>
            </div>

            <!-- Instance list -->
            <div v-if="host.instances.length > 0" class="fleet-host__instances">
              <div class="fleet-instances__label t-eyebrow">Game instances ({{ host.instances.length }})</div>
              <div class="fleet-instances__list">
                <div
                  v-for="inst in host.instances"
                  :key="inst.id"
                  class="fleet-instance"
                >
                  <StatusDot :tone="instanceTone(inst.state)" :size="7" />
                  <span class="fleet-instance__game">{{ inst.game }}</span>
                  <span v-if="inst.label" class="fleet-instance__label">{{ inst.label }}</span>
                  <Badge :tone="instanceTone(inst.state)" class="fleet-instance__badge">
                    {{ inst.state }}
                  </Badge>
                  <span class="fleet-instance__uptime">{{ formatUptime(inst.uptime_seconds) }}</span>
                  <span class="fleet-instance__seen">{{ safeDate(inst.last_seen_at) }}</span>
                </div>
              </div>
            </div>

            <!-- No instances under this host -->
            <div v-else class="fleet-host__no-instances">
              <Icon name="layers" :size="13" />
              <span>No game instances reported</span>
            </div>
          </template>
        </Panel>
      </div>
    </template>
  </div>
</template>

<style scoped>
/* ---- Page shell ---- */
.fleet-view {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 24px;
  max-width: 1100px;
}

.fleet-view__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.fleet-view__title {
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--text-primary);
  margin: 0;
}

.fleet-view__sub {
  font-size: var(--text-sm);
  color: var(--text-tertiary);
  margin: 2px 0 0;
}

.fleet-view__loading {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-tertiary);
  font-size: var(--text-sm);
  padding: 32px 0;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
.fleet-loading-icon {
  animation: spin 1s linear infinite;
  color: var(--accent);
}

/* ---- Summary strip ---- */
.fleet-view__summary {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

@media (max-width: 640px) {
  .fleet-view__summary { grid-template-columns: 1fr; }
}

/* ---- Host list ---- */
.fleet-view__hosts {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ---- Host card internals ---- */
.fleet-host__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 14px 16px 12px;
}

.fleet-host__identity {
  display: flex;
  align-items: center;
  gap: 9px;
}

.fleet-host__name {
  font-weight: 600;
  font-size: var(--text-base);
  color: var(--text-primary);
  font-family: var(--font-mono);
}

.fleet-host__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.fleet-host__meta-item {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
}

/* ---- Metrics row ---- */
.fleet-host__metrics {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}

.fleet-metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 16px;
  border-right: 1px solid var(--border-subtle);
  flex: 1;
  min-width: 110px;
}
.fleet-metric:last-child { border-right: none; }

.fleet-metric__label {
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.fleet-metric__value {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  display: flex;
  align-items: baseline;
  gap: 5px;
}

.fleet-metric__value--sm {
  font-size: 11px;
  font-weight: 400;
}

.fleet-metric__sub {
  font-size: var(--text-xs);
  color: var(--text-muted);
  font-weight: 400;
}

/* ---- Instance list ---- */
.fleet-host__instances {
  padding: 12px 16px 14px;
}

.fleet-instances__label {
  margin-bottom: 8px;
}

.fleet-instances__list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.fleet-instance {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 10px;
  background: var(--surface-raised-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
}

.fleet-instance__game {
  font-weight: 600;
  color: var(--text-primary);
  min-width: 60px;
}

.fleet-instance__label {
  color: var(--text-secondary);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.fleet-instance__badge {
  flex-shrink: 0;
}

.fleet-instance__uptime {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  min-width: 48px;
}

.fleet-instance__seen {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-left: auto;
}

/* ---- No instances ---- */
.fleet-host__no-instances {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 12px 16px 14px;
  font-size: var(--text-sm);
  color: var(--text-muted);
}
</style>
