<template>
  <div class="page">
    <!-- Toolbar -->
    <div class="toolbar">
      <div class="search-wrap">
        <el-icon class="search-icon"><Search /></el-icon>
        <el-input v-model="search" placeholder="搜索主机名" clearable @change="load" />
      </div>
      <el-select v-model="osFilter" placeholder="系统类型" clearable @change="load" class="os-select">
        <el-option label="Linux" value="Linux" />
        <el-option label="Windows" value="Windows" />
        <el-option label="macOS" value="macOS" />
        <el-option label="Other" value="Other" />
      </el-select>
      <el-button v-if="isAdmin" type="primary" :icon="Plus" @click="openEdit()">添加机器</el-button>
      <el-button v-if="isAdmin" type="success" @click="handleExport">导出数据</el-button>
      <el-button v-if="isAdmin" type="warning" @click="handleImportClick">导入数据</el-button>
      <input ref="importFileRef" type="file" accept=".json" style="display:none" @change="handleImportFile">
    </div>

    <!-- Cards -->
    <div class="cards-grid">
      <div v-for="m in machines" :key="m.id" class="server-card" :class="[{ 'guest-card': !isAdmin, 'offline-card': !m.is_online }]" @click="isAdmin && goDetail(m.id)">
        <div class="card-header">
          <div class="title-row">
            <span class="os-dot" :class="osClass(m.os_type)" :title="m.os_type"></span>
            <span class="hostname">{{ m.hostname }}</span>
            <span class="status-badge" :class="m.is_online ? 'online' : 'offline'">{{ m.is_online ? '在线' : '离线' }}</span>
            <el-tag v-if="m.service_count" size="small" effect="plain" class="svc-tag" round>服务 {{ m.service_count }}</el-tag>
          </div>
          <div class="meta-row">
            <span v-if="isAdmin" class="meta-ip">{{ m.ip }}</span>
            <span class="meta-item">{{ m.os_type }}</span>
            <span v-if="m.os_version" class="meta-item">{{ m.os_version }}</span>
            <span v-if="m.uptime" class="meta-uptime">{{ m.uptime }}</span>
          </div>
        </div>

        <div v-if="m.cpu_info || m.memory_info || m.disk_info" class="stats-row">
          <div v-if="m.cpu_info" class="stat-pill">
            <div class="pill-icon cpu">C</div>
            <div class="pill-body">
              <div class="pill-label">CPU</div>
              <div class="pill-bar"><div class="pill-fill" :style="{ width: extractPercent(m.cpu_info) + '%' }"></div></div>
            </div>
            <div class="pill-value">{{ extractPercent(m.cpu_info) }}%</div>
          </div>
          <div v-if="m.memory_info" class="stat-pill">
            <div class="pill-icon mem">M</div>
            <div class="pill-body">
              <div class="pill-label">RAM</div>
              <div class="pill-bar"><div class="pill-fill" :style="{ width: extractPercent(m.memory_info) + '%', background: getMemBarColor(extractPercent(m.memory_info)) }"></div></div>
            </div>
            <div class="pill-value">{{ extractDetail(m.memory_info) }}</div>
          </div>
          <div v-if="getMainDisk(m.disk_info)" class="stat-pill">
            <div class="pill-icon disk">D</div>
            <div class="pill-body">
              <div class="pill-label">DISK</div>
              <div class="pill-bar"><div class="pill-fill" :style="{ width: getMainDisk(m.disk_info).percent + '%', background: getDiskBarColor(getMainDisk(m.disk_info).percent) }"></div></div>
            </div>
            <div class="pill-value">{{ getMainDisk(m.disk_info).detail }}</div>
          </div>
        </div>

        <div v-if="m.listen_ports" class="ports-row">
          <el-tag v-for="p in m.listen_ports.split(',').slice(0,6)" :key="p" size="small" effect="plain" round class="port-tag">
            {{ p.trim() }}
          </el-tag>
          <span v-if="m.listen_ports.split(',').length > 6" class="more-ports">+{{ m.listen_ports.split(',').length - 6 }}</span>
        </div>

        <div v-if="m.ssh_synced_at" class="sync-time">
          同步于 {{ formatTime(m.ssh_synced_at) }}
        </div>
      </div>
    </div>

    <el-empty v-if="!machines.length" description="暂无机器" />

    <!-- Edit Dialog -->
    <el-dialog v-model="dialogVisible" :title="editing.id ? '编辑机器' : '添加机器'" width="480px" class="modern-dialog" destroy-on-close>
      <el-form :model="editing" label-width="90px">
        <el-form-item label="主机名" required>
          <el-input v-model="editing.hostname" placeholder="如 web-server-01" />
        </el-form-item>
        <el-form-item label="IP" required>
          <el-input v-model="editing.ip" placeholder="192.168.1.100" />
        </el-form-item>
        <el-form-item label="MAC">
          <el-input v-model="editing.mac" placeholder="AA:BB:CC:DD:EE:FF" />
        </el-form-item>
        <el-form-item label="系统" required>
          <el-select v-model="editing.os_type" style="width:100%">
            <el-option label="Linux" value="Linux" />
            <el-option label="Windows" value="Windows" />
            <el-option label="macOS" value="macOS" />
            <el-option label="Other" value="Other" />
          </el-select>
        </el-form-item>
        <el-form-item label="系统版本">
          <el-input v-model="editing.os_version" placeholder="Ubuntu 22.04" />
        </el-form-item>
        <el-divider content-position="left">SSH 配置</el-divider>
        <el-form-item label="SSH 端口">
          <el-input-number v-model="editing.ssh_port" :min="1" :max="65535" style="width:100%" />
        </el-form-item>
        <el-form-item label="SSH 用户">
          <el-input v-model="editing.ssh_username" placeholder="root" />
        </el-form-item>
        <el-form-item label="SSH 密码">
          <el-input v-model="editing.ssh_password" type="password" show-password placeholder="输入则更新密码，留空保持不变" />
        </el-form-item>
        <el-form-item label="备注">
          <el-input v-model="editing.notes" type="textarea" :rows="3" placeholder="记录用途、负责人等信息" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveMachine">保存</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { Plus, Search } from '@element-plus/icons-vue'
import { fetchMachines, createMachine, updateMachine, checkAuth, uiRefreshInterval, exportData, importData } from '@/api'
import { getAuth } from '@/router'
import { ElMessage } from 'element-plus'

const router = useRouter()
const isAdmin = getAuth().is_admin
const machines = ref([])
const search = ref('')
const osFilter = ref('')
const dialogVisible = ref(false)
const editing = ref({ hostname: '', ip: '', mac: '', os_type: 'Linux', os_version: '', notes: '', ssh_port: 22, ssh_username: '', ssh_password: '' })
const importFileRef = ref(null)
let timer = null

onMounted(async () => {
  await load()
  await checkAuth()
  const interval = uiRefreshInterval || 10000
  if (interval > 0) {
    timer = setInterval(load, interval)
  }
})

onUnmounted(() => {
  if (timer) clearInterval(timer)
})

async function load() {
  const res = await fetchMachines({ search: search.value, os_type: osFilter.value })
  machines.value = res.data
}

function goDetail(id) {
  router.push(`/machines/${id}`)
}

function openEdit(item) {
  editing.value = item
    ? { ...item, ssh_port: item.ssh_port || 22, ssh_username: item.ssh_username || '', ssh_password: '' }
    : { hostname: '', ip: '', mac: '', os_type: 'Linux', os_version: '', notes: '', ssh_port: 22, ssh_username: '', ssh_password: '' }
  dialogVisible.value = true
}

async function saveMachine() {
  const data = { ...editing.value }
  if (!data.hostname || !data.ip || !data.os_type) {
    ElMessage.warning('请填写必填项')
    return
  }
  if (data.id) {
    await updateMachine(data.id, data)
  } else {
    await createMachine(data)
  }
  ElMessage.success('保存成功')
  dialogVisible.value = false
  load()
}

async function handleExport() {
  try {
    const res = await exportData()
    const blob = new Blob([res.data])
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `lan-manager-backup-${new Date().toISOString().slice(0,10)}.json`
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    window.URL.revokeObjectURL(url)
    ElMessage.success('导出成功')
  } catch (e) {
    ElMessage.error('导出失败')
  }
}

function handleImportClick() {
  importFileRef.value?.click()
}

async function handleImportFile(e) {
  const file = e.target.files?.[0]
  if (!file) return
  const formData = new FormData()
  formData.append('file', file)
  formData.append('mode', 'overwrite')
  try {
    await importData(formData)
    ElMessage.success('导入成功，页面即将刷新')
    setTimeout(() => location.reload(), 800)
  } catch (err) {
    ElMessage.error('导入失败')
  }
  e.target.value = ''
}

function extractPercent(str) {
  if (!str) return 0
  const m = str.match(/\((\d+)%\)/) || str.match(/(\d+(?:\.\d+)?)%/)
  if (m) {
    const v = parseFloat(m[1])
    return isNaN(v) ? 0 : Math.min(100, Math.max(0, Math.round(v)))
  }
  return 0
}

function extractDetail(str) {
  if (!str) return ''
  const cpuCore = str.match(/\((\d+)\s*cores\)/)
  if (cpuCore) return cpuCore[1] + ' cores'
  let s = str.replace(/\s*\(\d+(?:\.\d+)?%\)\s*/g, '').trim()
  if (s.includes(',')) s = s.split(',')[0].trim()
  s = s.replace(/^\/\s+/, '').trim()
  if (/^\d+\.?\d*\/\d+\.?\d*$/.test(s)) s += 'G'
  return s
}

function getMemBarColor(v) {
  if (v >= 90) return '#f87171'
  if (v >= 70) return '#fbbf24'
  return '#34d399'
}
function getDiskBarColor(v) {
  if (v >= 90) return '#f87171'
  if (v >= 80) return '#fbbf24'
  return '#60a5fa'
}

function parseDisks(str) {
  if (!str) return []
  const disks = []
  const regex = /(\S+)\s+([\d\.]+[KMGT]?\/[\d\.]+[KMGT]?)\s+\((\d+)%\)/g
  let m
  while ((m = regex.exec(str)) !== null) {
    disks.push({ mount: m[1], detail: m[2], percent: parseInt(m[3], 10) })
  }
  if (disks.length > 0) {
    return disks
  }
  // fallback: single disk old format
  const pct = extractPercent(str)
  const det = extractDetail(str)
  if (det) {
    return [{ mount: '', detail: det, percent: pct }]
  }
  return []
}

function getMainDisk(str) {
  const disks = parseDisks(str)
  const root = disks.find(d => d.mount === '/')
  return root || null
}

function osClass(os) {
  const map = { Linux: 'os-linux', Windows: 'os-windows', macOS: 'os-macos' }
  return map[os] || 'os-other'
}

function formatTime(t) {
  if (!t) return '-'
  const d = new Date(t)
  if (isNaN(d.getTime())) return t
  const pad = n => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
}
</script>

<style scoped>
.toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 20px;
}
.search-wrap {
  position: relative;
  flex: 1;
  max-width: 360px;
}
.search-wrap :deep(.el-input__wrapper) {
  padding-left: 34px;
  border-radius: 10px;
  box-shadow: 0 0 0 1px var(--border) inset;
}
.search-icon {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  font-size: 16px;
  z-index: 1;
}
.os-select :deep(.el-input__wrapper) {
  border-radius: 10px;
  box-shadow: 0 0 0 1px var(--border) inset;
}

.cards-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
}
@media (max-width: 900px) {
  .cards-grid {
    grid-template-columns: 1fr;
  }
}

.server-card {
  background: var(--surface);
  border-radius: var(--radius);
  padding: 18px;
  box-shadow: var(--shadow);
  border: 1px solid var(--border);
  border-top-width: 3px;
  border-top-color: var(--border-strong);
  cursor: pointer;
  transition: transform .12s ease, box-shadow .2s ease, opacity .2s ease, border-color .2s ease;
}
.server-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg);
  border-color: var(--border-strong);
}
.server-card.guest-card {
  cursor: default;
}
.server-card.guest-card:hover {
  transform: none;
  box-shadow: var(--shadow);
  border-color: var(--border);
}

.server-card.offline-card {
  opacity: 0.65;
  border-top-color: var(--border);
}
.server-card.offline-card .hostname {
  color: var(--text-muted);
}

.card-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.title-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.os-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
}
.os-dot.os-linux { background: #3b82f6; }
.os-dot.os-windows { background: #22c55e; }
.os-dot.os-macos { background: #a855f7; }
.os-dot.os-other { background: #64748b; }

.status-badge {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 600;
  line-height: 1;
}
.status-badge.online {
  background: rgba(34,197,94,0.12);
  color: #15803d;
}
.status-badge.offline {
  background: rgba(239,68,68,0.10);
  color: #b91c1c;
}
html.dark .status-badge.online {
  background: rgba(52,211,153,0.15);
  color: #34d399;
}
html.dark .status-badge.offline {
  background: rgba(248,113,113,0.15);
  color: #f87171;
}

.hostname {
  font-weight: 700;
  font-size: 15px;
  color: var(--text);
}
.svc-tag {
  font-size: 11px;
  height: 18px;
  padding: 0 8px;
  color: var(--text-secondary);
  border-color: var(--border);
}

.meta-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 0;
  font-size: 12px;
  color: var(--text-secondary);
}
.meta-ip {
  color: var(--text);
  font-weight: 500;
  background: var(--surface-hover);
  border: 1px solid var(--border);
  padding: 1px 6px;
  border-radius: 4px;
}
.meta-item + .meta-item::before,
.meta-uptime::before {
  content: '·';
  margin: 0 6px;
  color: var(--text-muted);
}
.meta-uptime { color: var(--success); }

.stats-row {
  margin-top: 14px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.stat-pill {
  background: var(--pill-bg);
  border: 1px solid var(--pill-border);
  border-radius: 10px;
  padding: 9px 10px;
  color: var(--pill-text);
  display: flex;
  align-items: center;
  gap: 8px;
  transition: background .2s ease, border-color .2s ease;
}
.pill-icon {
  width: 22px; height: 22px; border-radius: 5px;
  display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 800;
  background: rgba(255,255,255,0.10);
  flex-shrink: 0;
}
.pill-icon.cpu { color: #7dd3fc; }
.pill-icon.mem { color: #86efac; }
.pill-icon.disk { color: #93c5fd; }
.pill-body {
  flex: 1; display: flex; flex-direction: column; gap: 4px; min-width: 0;
}
.pill-label { font-size: 10px; color: rgba(255,255,255,0.85); font-weight: 600; }
.pill-bar { height: 3px; background: rgba(255,255,255,0.15); border-radius: 2px; overflow: hidden; }
.pill-fill { height: 100%; border-radius: 2px; background: rgba(255,255,255,0.9); transition: width .3s ease; }
.pill-value { font-size: 11px; font-weight: 700; color: #fff; white-space: nowrap; }

.ports-row {
  margin-top: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.port-tag {
  font-size: 11px;
  height: 20px;
  padding: 0 8px;
  color: var(--text-secondary);
  border-color: var(--border);
}
.more-ports { font-size: 11px; color: var(--text-muted); line-height: 20px; }

.sync-time {
  margin-top: 10px;
  font-size: 11px;
  color: var(--text-muted);
}
</style>
