<template>
  <div class="container">
    <div class="page-toolbar">
      <div class="toolbar-left">
        <span class="toolbar-label">当前服务器</span>
        <a-select
          v-model="selectedServerIdentity"
          class="server-select"
          placeholder="输入名称或地址搜索"
          allow-search
          allow-clear
          :loading="serverListLoading"
          :filter-option="filterServerOption"
        >
          <a-option
            v-for="item in serverOptions"
            :key="item.server_identity"
            :value="item.server_identity"
            :label="`${item.name} ${item.host} ${item.ip}`"
          >
            <div class="server-option">
              <span class="server-option-name">{{ item.name }}</span>
              <span class="server-option-ip">{{ item.ip }}</span>
            </div>
          </a-option>
        </a-select>
      </div>
      <div v-if="activeServer" class="toolbar-meta text-muted">
        {{ activeServer.os }} · {{ activeServer.location }}
      </div>
    </div>

    <!-- 资源统计 -->
    <a-spin :loading="statsLoading" style="width: 100%">
    <a-row :gutter="16" class="stats-row">
      <a-col :xs="24" :sm="12" :lg="6">
        <a-card class="stats-card" :bordered="false">
          <div class="stats-content">
            <div class="stats-icon stats-icon-primary">
              <icon-storage />
            </div>
            <div class="stats-info">
              <div class="stats-title">内存</div>
              <div class="stats-value">{{ stats.memory.util }}%</div>
              <div class="stats-desc">
                总值 {{ stats.memory.total }} · 已用 {{ stats.memory.used }}
              </div>
            </div>
          </div>
        </a-card>
      </a-col>
      <a-col :xs="24" :sm="12" :lg="6">
        <a-card class="stats-card" :bordered="false">
          <div class="stats-content">
            <div class="stats-icon stats-icon-cyan">
              <icon-drive-file />
            </div>
            <div class="stats-info">
              <div class="stats-title">磁盘（系统盘 /）</div>
              <div class="stats-value">{{ stats.systemDisk.util }}%</div>
              <div class="stats-desc">
                总值 {{ stats.systemDisk.total }} · 已用 {{ stats.systemDisk.used }}
              </div>
            </div>
          </div>
        </a-card>
      </a-col>
      <a-col :xs="24" :sm="12" :lg="6">
        <a-card class="stats-card" :bordered="false">
          <div class="stats-content">
            <div class="stats-icon stats-icon-green">
              <icon-code-square />
            </div>
            <div class="stats-info">
              <div class="stats-title">CPU</div>
              <div class="stats-value">{{ stats.cpu.util }}%</div>
              <div class="stats-desc">
                总值 {{ stats.cpu.total }} · 已用 {{ stats.cpu.used }}
              </div>
            </div>
          </div>
        </a-card>
      </a-col>
      <a-col :xs="24" :sm="12" :lg="6">
        <a-card class="stats-card" :bordered="false">
          <div class="stats-content">
            <div class="stats-icon stats-icon-purple">
              <icon-folder />
            </div>
            <div class="stats-info">
              <div class="stats-title">硬盘（数据盘汇总）</div>
              <div class="stats-value">{{ stats.dataDisk.util }}%</div>
              <div class="stats-desc">
                总值 {{ stats.dataDisk.total }} · 已用 {{ stats.dataDisk.used }}
              </div>
            </div>
          </div>
        </a-card>
      </a-col>
    </a-row>
    </a-spin>

    <a-row :gutter="16" class="chart-row">
      <a-col :xs="24" :lg="14">
        <a-card title="网络流量（近 6 小时）" :bordered="false">
          <template #extra>
            <a-space>
              <span class="legend-item">
                <span class="legend-dot legend-dot-1"></span>
                <span>接收</span>
              </span>
              <span class="legend-item">
                <span class="legend-dot legend-dot-2"></span>
                <span>发送</span>
              </span>
            </a-space>
          </template>
          <a-spin :loading="chartLoading" class="chart-spin">
            <p v-if="trafficHint" class="traffic-hint text-muted">{{ trafficHint }}</p>
            <div class="chart-container">
              <Chart :options="trafficChartOptions" height="300px" />
            </div>
          </a-spin>
        </a-card>
      </a-col>
      <a-col :xs="24" :lg="10">
        <a-card title="最近告警" :bordered="false">
          <template #extra>
            <span class="text-muted">当前主机 · 最近 5 条</span>
          </template>
          <a-spin :loading="alertsLoading" class="alert-spin">
            <div v-if="recentAlerts.length" class="alert-list">
              <div
                v-for="item in recentAlerts"
                :key="item.id"
                class="alert-item"
              >
                <div class="alert-item-top">
                  <span class="alert-item-name" :title="item.alert_name">{{
                    item.alert_name || '-'
                  }}</span>
                  <a-tag
                    v-if="item.severity"
                    size="small"
                    :color="item.severity.color || undefined"
                  >
                    {{ item.severity.name }}
                  </a-tag>
                </div>
                <div class="alert-item-meta">
                  <a-tag size="small" :color="alertStatusColor(item.status)">
                    {{ alertStatusText(item.status) }}
                  </a-tag>
                  <span class="alert-item-time">{{
                    formatAlertTime(item.starts_at)
                  }}</span>
                </div>
                <div v-if="item.summary" class="alert-item-summary text-muted">
                  {{ item.summary }}
                </div>
              </div>
            </div>
            <a-empty
              v-else
              class="alert-empty"
              description="暂无与该主机相关的告警"
            />
          </a-spin>
        </a-card>
      </a-col>
    </a-row>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue'
import dayjs from 'dayjs'
import { Message } from '@arco-design/web-vue'
import {
  IconStorage,
  IconDriveFile,
  IconCodeSquare,
  IconFolder,
} from '@arco-design/web-vue/es/icon'
import Chart from '@/components/chart/index.vue'
import {
  fetchServerList,
  fetchServerMetricsSummary,
  fetchServerNetworkTraffic,
  type HostMetricsDiskMount,
  type HostMetricsSummary,
  type HostNetworkTrafficPayload,
  type ServerItem,
} from '@/api/ops/server'
import { fetchAlertRecords } from '@/api/ops/alertRecord'

interface ServerOption {
  server_identity: string
  name: string
  host: string
  ip: string
  os: string
  location: string
}

interface MetricBlock {
  total: string
  used: string
  util: number
}

interface ServerMetrics {
  memory: MetricBlock
  systemDisk: MetricBlock
  cpu: MetricBlock
  dataDisk: MetricBlock
}

const serverListLoading = ref(false)
const statsLoading = ref(false)
const chartLoading = ref(false)
const alertsLoading = ref(false)

/** 与当前选中主机相关的最近告警（来自 Alert record/list） */
const recentAlerts = ref<any[]>([])

const serverOptions = ref<ServerOption[]>([])
const selectedServerIdentity = ref<string | undefined>(undefined)

const summaryPayload = ref<HostMetricsSummary | null>(null)
const trafficPayload = ref<HostNetworkTrafficPayload | null>(null)

const activeServer = computed(() =>
  serverOptions.value.find((s) => s.server_identity === selectedServerIdentity.value),
)

const defaultMetrics: ServerMetrics = {
  memory: { total: '-', used: '-', util: 0 },
  systemDisk: { total: '-', used: '-', util: 0 },
  cpu: { total: '-', used: '-', util: 0 },
  dataDisk: { total: '-', used: '-', util: 0 },
}

function round1(n: number) {
  return Math.round(n * 10) / 10
}

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

function useStatToBlock(
  m: { total_bytes: number; used_bytes: number; used_percent: number } | undefined,
): MetricBlock {
  if (!m) return { total: '-', used: '-', util: 0 }
  return {
    total: formatBytes(m.total_bytes),
    used: formatBytes(m.used_bytes),
    util: round1(m.used_percent),
  }
}

function diskMountToBlock(d: HostMetricsDiskMount | undefined): MetricBlock {
  if (!d) return { total: '-', used: '-', util: 0 }
  return {
    total: formatBytes(d.total_bytes),
    used: formatBytes(d.used_bytes),
    util: round1(d.used_percent),
  }
}

function aggregateDataDisks(disks: HostMetricsDiskMount[] | undefined): MetricBlock {
  if (!disks?.length) return { total: '-', used: '-', util: 0 }
  let total = 0
  let used = 0
  for (const d of disks) {
    total += d.total_bytes
    used += d.used_bytes
  }
  if (total <= 0) return { total: '-', used: '-', util: 0 }
  return {
    total: formatBytes(total),
    used: formatBytes(used),
    util: round1((used / total) * 100),
  }
}

const stats = computed<ServerMetrics>(() => {
  const d = summaryPayload.value
  if (!d || !d.has_data) {
    return defaultMetrics
  }
  const cores = d.cpu?.logical_cores_total ?? 0
  const usage = d.cpu?.usage_percent ?? 0
  const usedCores = cores > 0 ? (usage / 100) * cores : 0
  const cpuBlock: MetricBlock = {
    total: cores > 0 ? `${round1(cores)} 核` : '-',
    used: cores > 0 ? `约 ${round1(usedCores)} 核` : '-',
    util: round1(usage),
  }
  return {
    memory: useStatToBlock(d.memory),
    systemDisk: diskMountToBlock(d.disk_root),
    cpu: cpuBlock,
    dataDisk: aggregateDataDisks(d.data_disks),
  }
})

function filterServerOption(input: string, option: { label?: string }) {
  if (!input) return true
  const q = input.trim().toLowerCase()
  const label = String(option?.label ?? '').toLowerCase()
  return label.includes(q)
}

/** 判断告警记录是否属于当前主机（labels 或文本中出现 identity / IP / hostname） */
function matchServerAlert(record: any, server: ServerOption): boolean {
  try {
    const raw = record?.labels
    const labels =
      typeof raw === 'string' && raw
        ? JSON.parse(raw)
        : raw && typeof raw === 'object'
          ? raw
          : null
    if (labels && typeof labels === 'object') {
      if (labels.server_identity === server.server_identity) return true
      const inst = String(labels.instance ?? '')
      if (inst && (inst === server.ip || inst === server.host)) return true
    }
  } catch {
    /* ignore */
  }
  const text = `${record?.alert_name ?? ''} ${record?.summary ?? ''} ${record?.description ?? ''}`
  return (
    text.includes(server.server_identity) ||
    (!!server.ip && server.ip !== '-' && text.includes(server.ip)) ||
    (!!server.host && text.includes(server.host))
  )
}

function formatAlertTime(iso: string | undefined | null) {
  if (!iso) return '-'
  return dayjs(iso).format('MM-DD HH:mm')
}

function alertStatusColor(status: string | undefined) {
  const map: Record<string, string> = {
    firing: 'red',
    pending: 'orange',
    acked: 'gold',
    resolved: 'green',
    silenced: 'gray',
    suppressed: 'lightgray',
  }
  return map[status || ''] || 'blue'
}

function alertStatusText(status: string | undefined) {
  const map: Record<string, string> = {
    firing: '告警中',
    pending: '待处理',
    acked: '已确认',
    resolved: '已解决',
    silenced: '已屏蔽',
    suppressed: '已抑制',
  }
  return map[status || ''] || status || '-'
}

async function loadRecentAlertsForServer(server: ServerOption | undefined) {
  if (!server) {
    recentAlerts.value = []
    return
  }
  try {
    const res: any = await fetchAlertRecords({
      page: 1,
      page_size: 100,
      sort: 'starts_at',
      order: 'desc',
    })
    if (res.code !== 0) {
      recentAlerts.value = []
      Message.error(res.message || '加载最近告警失败')
      return
    }
    const rows: any[] = res.details?.data ?? []
    recentAlerts.value = rows.filter((r) => matchServerAlert(r, server)).slice(0, 5)
  } catch (e: any) {
    recentAlerts.value = []
    Message.error(e?.message || '加载最近告警失败')
  }
}

/** 是否已有速率曲线数据（兼容旧接口仅看 points） */
const hasTrafficRateSeries = computed(() => {
  const p = trafficPayload.value
  if (!p) return false
  if (typeof p.has_rate_series === 'boolean') return p.has_rate_series
  return (p.points?.length ?? 0) > 0
})

const trafficHint = computed(() => {
  const p = trafficPayload.value
  if (!p || chartLoading.value) return ''
  if (hasTrafficRateSeries.value) return ''
  if (p.has_data) {
    return p.note || '已有指标，需至少两次采集后才展示速率曲线'
  }
  return '暂无主机网络累计指标（control_host_metrics_data），请确认主机采集与落库。'
})

const trafficChartOptions = computed(() => {
  const pts = trafficPayload.value?.points ?? []
  if (!pts.length) {
    return {
      tooltip: { trigger: 'axis' },
      legend: { show: false },
      grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
      xAxis: { type: 'category', boundaryGap: false, data: ['-'] },
      yAxis: { type: 'value', name: 'Mb/s' },
      series: [
        {
          name: '接收',
          type: 'line',
          smooth: true,
          data: [0],
          lineStyle: { width: 2, color: '#165DFF' },
          itemStyle: { color: '#165DFF' },
        },
        {
          name: '发送',
          type: 'line',
          smooth: true,
          data: [0],
          lineStyle: { width: 2, color: '#14C9C9' },
          itemStyle: { color: '#14C9C9' },
        },
      ],
    }
  }
  const labels = pts.map((p) => dayjs(p.time).format('MM-DD HH:mm'))
  const rx = pts.map((p) => round1(p.recv_mbps))
  const tx = pts.map((p) => round1(p.send_mbps))
  return {
    tooltip: { trigger: 'axis' },
    legend: { show: false },
    grid: {
      left: '3%',
      right: '4%',
      bottom: '3%',
      containLabel: true,
    },
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: labels,
    },
    yAxis: {
      type: 'value',
      name: 'Mb/s',
    },
    series: [
      {
        name: '接收',
        type: 'line',
        smooth: true,
        data: rx,
        areaStyle: { opacity: 0.08 },
        lineStyle: { width: 2, color: '#165DFF' },
        itemStyle: { color: '#165DFF' },
      },
      {
        name: '发送',
        type: 'line',
        smooth: true,
        data: tx,
        areaStyle: { opacity: 0.08 },
        lineStyle: { width: 2, color: '#14C9C9' },
        itemStyle: { color: '#14C9C9' },
      },
    ],
  }
})

async function loadServerOptions() {
  serverListLoading.value = true
  try {
    const res: any = await fetchServerList({ page: 1, size: 500 })
    if (res.code !== 0) {
      Message.error(res.message || '加载服务器列表失败')
      serverOptions.value = []
      return
    }
    const list: ServerItem[] = res.details?.data ?? []
    serverOptions.value = list.map((r) => ({
      server_identity: r.server_identity,
      name: r.name,
      host: r.host,
      ip: r.ip_address || r.host,
      os: [r.os, r.os_version].filter(Boolean).join(' ') || '-',
      location: r.location || '-',
    }))
    if (
      selectedServerIdentity.value &&
      !serverOptions.value.some((s) => s.server_identity === selectedServerIdentity.value)
    ) {
      selectedServerIdentity.value = undefined
    }
    if (!selectedServerIdentity.value && serverOptions.value.length > 0) {
      selectedServerIdentity.value = serverOptions.value[0].server_identity
    }
  } catch (e: any) {
    Message.error(e?.message || '加载服务器列表失败')
    serverOptions.value = []
  } finally {
    serverListLoading.value = false
  }
}

async function refreshDashboard() {
  const sid = selectedServerIdentity.value
  if (!sid) {
    summaryPayload.value = null
    trafficPayload.value = null
    recentAlerts.value = []
    return
  }
  const server = serverOptions.value.find((s) => s.server_identity === sid)
  statsLoading.value = true
  chartLoading.value = true
  alertsLoading.value = true
  try {
    const [sumRes, trafRes]: any[] = await Promise.all([
      fetchServerMetricsSummary(sid),
      fetchServerNetworkTraffic(sid, 6),
      loadRecentAlertsForServer(server),
    ])
    if (sumRes.code === 0) {
      summaryPayload.value = sumRes.details ?? null
    } else {
      summaryPayload.value = null
      Message.error(sumRes.message || '加载统计卡片失败')
    }
    if (trafRes.code === 0) {
      trafficPayload.value = trafRes.details ?? null
    } else {
      trafficPayload.value = null
      Message.error(trafRes.message || '加载网络流量失败')
    }
  } catch (e: any) {
    Message.error(e?.message || '请求失败')
    summaryPayload.value = null
    trafficPayload.value = null
    recentAlerts.value = []
  } finally {
    statsLoading.value = false
    chartLoading.value = false
    alertsLoading.value = false
  }
}

watch(selectedServerIdentity, (sid) => {
  if (!sid) {
    summaryPayload.value = null
    trafficPayload.value = null
    recentAlerts.value = []
    return
  }
  refreshDashboard()
})

onMounted(async () => {
  await loadServerOptions()
  // 首次选中由 loadServerOptions 设置，watch(selectedServerIdentity) 会拉取指标
})
</script>

<script lang="ts">
export default {
  name: 'OsMonitor',
}
</script>

<style scoped lang="less">
.container {
  padding: 16px;
}

.page-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.toolbar-left {
  display: flex;
  align-items: center;
  gap: 12px;
}

.toolbar-label {
  font-size: 14px;
  color: var(--color-text-2);
  white-space: nowrap;
}

.server-select {
  min-width: 260px;
  max-width: 360px;
}

.server-option {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
}

.server-option-name {
  font-size: 14px;
  color: var(--color-text-1);
}

.server-option-ip {
  font-size: 13px;
  color: var(--color-text-3);
  font-family: var(--font-family-mono);
}

.toolbar-meta {
  font-size: 13px;
}

.stats-row {
  margin-bottom: 16px;
}

.stats-card {
  height: 100%;

  :deep(.arco-card-body) {
    padding: 16px;
  }

  .stats-content {
    display: flex;
    align-items: flex-start;
    gap: 12px;
  }

  .stats-icon {
    width: 48px;
    height: 48px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;

    &-primary {
      background-color: rgba(22, 93, 255, 0.1);
      color: rgb(var(--primary-6));
    }

    &-cyan {
      background-color: rgba(20, 201, 201, 0.1);
      color: #14c9c9;
    }

    &-green {
      background-color: rgba(0, 180, 42, 0.1);
      color: rgb(var(--success-6));
    }

    &-purple {
      background-color: rgba(114, 46, 209, 0.1);
      color: #722ed1;
    }
  }

  .stats-info {
    flex: 1;
    min-width: 0;
  }

  .stats-title {
    font-size: 14px;
    color: var(--color-text-3);
    margin-bottom: 4px;
  }

  .stats-value {
    font-size: 24px;
    font-weight: 600;
    color: var(--color-text-1);
    line-height: 1.2;
    margin-bottom: 4px;
  }

  .stats-desc {
    font-size: 12px;
    color: var(--color-text-3);
    line-height: 1.5;
  }
}

.chart-row {
  margin-bottom: 16px;
}

.chart-container {
  height: 300px;
}

.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--color-text-3);
}

.legend-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;

  &-1 {
    background-color: #165dff;
  }

  &-2 {
    background-color: #14c9c9;
  }
}

.chart-spin {
  display: block;
  width: 100%;
}

.alert-spin {
  display: block;
  min-height: 120px;
}

.alert-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.alert-item {
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--color-fill-1);
  border: 1px solid var(--color-border-1);
}

.alert-item-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 6px;
}

.alert-item-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-text-1);
  line-height: 1.4;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.alert-item-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 4px;
}

.alert-item-time {
  font-size: 12px;
  color: var(--color-text-3);
}

.alert-item-summary {
  font-size: 12px;
  line-height: 1.5;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.alert-empty {
  padding: 32px 0;
}

.text-muted {
  font-size: 12px;
  color: var(--color-text-3);
}
</style>
