<template>
  <div class="vm-monitor-container">
    <div class="monitor-toolbar">
      <div class="toolbar-row">
        <div class="toolbar-item">
          <span class="toolbar-label">虚拟机</span>
          <el-select
            v-model="selectedVms"
            multiple
            collapse-tags
            collapse-tags-tooltip
            placeholder="选择要监控的虚拟机"
            style="width: 360px"
            filterable
            :loading="vmListLoading"
          >
            <el-option v-for="vm in vmOptions" :key="vm.name" :label="`${vm.name} (ID:${vm.id})`" :value="vm.name" />
          </el-select>
          <el-button link type="primary" @click="selectAllVms" v-if="vmOptions.length > 0">全选</el-button>
          <el-button link @click="selectedVms = []" v-if="selectedVms.length > 0">清空</el-button>
        </div>
        <div class="toolbar-item">
          <span class="toolbar-label">指标</span>
          <el-checkbox-group v-model="selectedMetrics">
            <el-checkbox label="cpu">CPU</el-checkbox>
            <el-checkbox label="memory">内存</el-checkbox>
            <el-checkbox label="disk">磁盘IO</el-checkbox>
            <el-checkbox label="network">网络</el-checkbox>
          </el-checkbox-group>
        </div>
      </div>
      <div class="toolbar-row">
        <div class="toolbar-item">
          <span class="toolbar-label">时间</span>
          <el-radio-group v-model="timeMode" size="small" @change="handleRefresh">
            <el-radio-button label="relative">最近</el-radio-button>
            <el-radio-button label="fixed">自定义</el-radio-button>
          </el-radio-group>
          <el-select v-if="timeMode === 'relative'" v-model="timeRange" style="width: 130px" @change="handleRefresh">
            <el-option label="10分钟" :value="10" />
            <el-option label="30分钟" :value="30" />
            <el-option label="1小时" :value="60" />
            <el-option label="3小时" :value="180" />
            <el-option label="6小时" :value="360" />
            <el-option label="12小时" :value="720" />
            <el-option label="24小时" :value="1440" />
          </el-select>
          <el-date-picker
            v-else
            v-model="fixedDateRange"
            type="datetimerange"
            range-separator="至"
            start-placeholder="开始"
            end-placeholder="结束"
            size="small"
            style="width: 340px"
            @change="handleRefresh"
          />
        </div>
        <div class="toolbar-item">
          <span class="toolbar-label">自动刷新</span>
          <el-select v-model="autoRefreshInterval" style="width: 110px" @change="resetAutoRefresh">
            <el-option label="关闭" :value="0" />
            <el-option label="10秒" :value="10" />
            <el-option label="30秒" :value="30" />
            <el-option label="1分钟" :value="60" />
            <el-option label="5分钟" :value="300" />
          </el-select>
        </div>
        <div class="toolbar-item">
          <el-button type="primary" @click="handleRefresh" :loading="metricsLoading" :disabled="selectedVms.length === 0">
            刷新
          </el-button>
        </div>
      </div>
    </div>

    <div class="charts-area" v-if="hasData">
      <div class="chart-section" v-for="metric in selectedMetrics" :key="metric">
        <h3 class="chart-section-title">{{ metricLabels[metric] }}</h3>
        <div class="chart-wrapper">
          <div class="chart-box" :ref="el => setChartRef(metric, el)"></div>
        </div>
      </div>
    </div>

    <el-empty v-else-if="!metricsLoading && selectedVms.length === 0" description="请选择要监控的虚拟机" :image-size="80" />
    <el-empty v-else-if="!metricsLoading && loaded && !hasData" description="暂无监控数据" :image-size="80" />
  </div>
</template>

<script setup>
import { ref, computed, inject, onMounted, onBeforeUnmount, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import { getVmList, getMetricsHistory } from '@/api/admin/kvmService'
import { extractApiError } from '@/utils/kvmErrorUtil'
import * as echarts from 'echarts'

const serviceId = inject('serviceId')
const hostId = inject('hostId')

const vmListLoading = ref(false)
const vmOptions = ref([])
const selectedVms = ref([])
const selectedMetrics = ref(['cpu', 'memory'])
const metricsLoading = ref(false)
const loaded = ref(false)
const metricsDataMap = ref({})
const timeMode = ref('relative')
const timeRange = ref(60)
const fixedDateRange = ref(null)
const autoRefreshInterval = ref(0)
let autoRefreshTimer = null

const metricLabels = {
  cpu: 'CPU 使用率',
  memory: '内存使用',
  disk: '磁盘IO速率',
  network: '网络速率'
}

const vmColors = ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#b37feb', '#36cfc9', '#ff85c0', '#ffc53d', '#597ef7']

const hasData = computed(() => Object.keys(metricsDataMap.value).length > 0)

const chartInstances = {}
const chartElements = {}

const setChartRef = (metric, el) => {
  if (el) {
    chartElements[metric] = el
    nextTick(() => {
      if (!chartInstances[metric]) {
        chartInstances[metric] = echarts.init(el)
      }
    })
  }
}

const selectAllVms = () => { selectedVms.value = vmOptions.value.map(v => v.name) }

const calcInterval = (ms) => {
  if (ms <= 10 * 60 * 1000) return '1m'
  if (ms <= 30 * 60 * 1000) return '3m'
  if (ms <= 60 * 60 * 1000) return '5m'
  if (ms <= 3 * 3600 * 1000) return '10m'
  if (ms <= 6 * 3600 * 1000) return '20m'
  if (ms <= 12 * 3600 * 1000) return '30m'
  if (ms <= 24 * 3600 * 1000) return '1h'
  return '3h'
}

const formatBytes = (val) => {
  if (!val && val !== 0) return '0 B'
  val = Math.abs(Number(val))
  if (val >= 1073741824) return (val / 1073741824).toFixed(2) + ' GB'
  if (val >= 1048576) return (val / 1048576).toFixed(2) + ' MB'
  if (val >= 1024) return (val / 1024).toFixed(1) + ' KB'
  return val.toFixed(0) + ' B'
}

const formatBytesPerSec = (val) => formatBytes(val) + '/s'

const formatMemKiB = (kib) => {
  if (!kib && kib !== 0) return '0'
  kib = Math.abs(Number(kib))
  if (kib >= 1048576) return (kib / 1048576).toFixed(1) + ' GB'
  if (kib >= 1024) return (kib / 1024).toFixed(0) + ' MB'
  return kib.toFixed(0) + ' KB'
}

const loadVmList = async () => {
  if (!serviceId.value || !hostId.value) return
  vmListLoading.value = true
  try {
    const res = await getVmList({ service_id: serviceId.value, host_id: hostId.value, page: 1, count: 500 })
    const body = res?.data
    if (body?.code === 200 && body?.data) {
      const inner = body.data
      vmOptions.value = inner.data || inner.vms || (Array.isArray(inner) ? inner : [])
    }
  } catch (e) {
    ElMessage.error(extractApiError(e?.response?.data, '获取虚拟机列表失败'))
  } finally {
    vmListLoading.value = false
  }
}

const getTimeRange = () => {
  if (timeMode.value === 'relative') {
    const endTime = new Date()
    const startTime = new Date(endTime - timeRange.value * 60 * 1000)
    return { startTime, endTime }
  } else {
    if (!fixedDateRange.value || fixedDateRange.value.length < 2) return null
    return { startTime: new Date(fixedDateRange.value[0]), endTime: new Date(fixedDateRange.value[1]) }
  }
}

const handleRefresh = async () => {
  if (!selectedVms.value.length) return
  const range = getTimeRange()
  if (!range) { ElMessage.warning('请选择时间范围'); return }
  metricsLoading.value = true
  const { startTime, endTime } = range
  const interval = calcInterval(endTime - startTime)

  const dataMap = {}
  try {
    await Promise.all(selectedVms.value.map(async (vmName) => {
      try {
        const res = await getMetricsHistory({
          service_id: serviceId.value,
          host_id: hostId.value,
          vm_name: vmName,
          start: startTime.toISOString(),
          end_time: endTime.toISOString(),
          interval
        })
        const body = res?.data
        if (body?.code === 200 && body?.data) {
          dataMap[vmName] = Array.isArray(body.data) ? body.data : (body.data.data || [])
        }
      } catch (e) {
        console.warn(`获取 ${vmName} 监控数据失败:`, e)
      }
    }))
    metricsDataMap.value = dataMap
    loaded.value = true
    await nextTick()
    renderCharts()
  } catch (e) {
    ElMessage.error('加载监控数据失败')
  } finally {
    metricsLoading.value = false
  }
}

const renderCharts = () => {
  const range = getTimeRange()
  const spanMs = range ? (range.endTime - range.startTime) : 0
  const showDate = spanMs >= 12 * 3600 * 1000
  const labelRotate = showDate ? 30 : 0

  for (const metric of selectedMetrics.value) {
    const chart = chartInstances[metric]
    if (!chart) continue

    const allTimes = getUnifiedTimeline()
    if (!allTimes.length) continue

    const timeLabels = allTimes.map(t => {
      const date = new Date(t)
      if (showDate) return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
      return date.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })
    })

    const seriesList = []
    selectedVms.value.forEach((vmName, idx) => {
      const metrics = metricsDataMap.value[vmName]
      if (!metrics || !metrics.length) return
      const color = vmColors[idx % vmColors.length]
      const vmData = buildMetricData(metric, metrics, allTimes)
      seriesList.push({ name: vmName, data: vmData, color })
    })

    if (!seriesList.length) continue

    let yAxisFormatter, tooltipUnit
    if (metric === 'cpu') {
      yAxisFormatter = v => v.toFixed(0) + '%'
      tooltipUnit = '%'
    } else if (metric === 'memory') {
      yAxisFormatter = v => formatMemKiB(v)
      tooltipUnit = 'KiB'
    } else if (metric === 'disk') {
      yAxisFormatter = v => formatBytesPerSec(v)
      tooltipUnit = '/s'
    } else {
      yAxisFormatter = v => formatBytesPerSec(v)
      tooltipUnit = '/s'
    }

    chart.setOption({
      tooltip: {
        trigger: 'axis',
        formatter: (params) => {
          let s = params[0]?.axisValue || ''
          params.forEach(p => {
            let val
            if (metric === 'cpu') val = (p.value ?? 0).toFixed(1) + '%'
            else if (metric === 'memory') val = formatMemKiB(p.value)
            else val = formatBytesPerSec(p.value)
            s += `<br/>${p.marker} ${p.seriesName}: ${val}`
          })
          return s
        }
      },
      legend: { top: 4, right: 8, textStyle: { fontSize: 11 }, type: 'scroll' },
      grid: { top: 40, left: 70, right: 16, bottom: labelRotate > 0 ? 55 : 35 },
      xAxis: {
        type: 'category', data: timeLabels, boundaryGap: false,
        axisLabel: { fontSize: 10, rotate: labelRotate, color: '#86909c' },
        axisLine: { lineStyle: { color: '#e8e8e8' } }
      },
      yAxis: {
        type: 'value', min: 0,
        max: metric === 'cpu' ? 100 : undefined,
        axisLabel: { fontSize: 10, formatter: yAxisFormatter, color: '#86909c' },
        splitLine: { lineStyle: { color: '#f0f0f0' } }
      },
      series: seriesList.map(s => ({
        name: s.name,
        type: 'line',
        data: s.data,
        smooth: true,
        symbol: 'none',
        lineStyle: { width: 1.5 },
        itemStyle: { color: s.color },
        areaStyle: seriesList.length <= 3
          ? { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: s.color + '20' }, { offset: 1, color: s.color + '02' }]) }
          : undefined
      }))
    }, true)
    chart.resize()
  }
}

const getUnifiedTimeline = () => {
  const timeSet = new Set()
  for (const vmName of selectedVms.value) {
    const data = metricsDataMap.value[vmName]
    if (data) data.forEach(m => timeSet.add(m.bucket))
  }
  return Array.from(timeSet).sort()
}

const buildMetricData = (metric, metrics, allTimes) => {
  const timeMap = new Map()
  metrics.forEach((m, i) => timeMap.set(m.bucket, { ...m, _idx: i }))

  return allTimes.map((t, tIdx) => {
    const m = timeMap.get(t)
    if (!m) return null

    if (metric === 'cpu') {
      return +(m.cpu_usage ?? 0).toFixed(1)
    } else if (metric === 'memory') {
      return +(m.mem_used ?? 0)
    } else if (metric === 'disk') {
      // disk_read/disk_write are cumulative Bytes, compute rate via diff
      if (m._idx === 0) return 0
      const prev = metrics[m._idx - 1]
      const dt = (new Date(m.bucket) - new Date(prev.bucket)) / 1000
      if (dt <= 0) return 0
      const readRate = Math.max(0, ((m.disk_read ?? 0) - (prev.disk_read ?? 0)) / dt)
      const writeRate = Math.max(0, ((m.disk_write ?? 0) - (prev.disk_write ?? 0)) / dt)
      return +(readRate + writeRate).toFixed(0)
    } else if (metric === 'network') {
      // net_rx/net_tx are already Bytes/s
      return +((m.net_rx ?? 0) + (m.net_tx ?? 0)).toFixed(0)
    }
    return 0
  })
}

const resetAutoRefresh = () => {
  if (autoRefreshTimer) { clearInterval(autoRefreshTimer); autoRefreshTimer = null }
  if (autoRefreshInterval.value > 0) {
    autoRefreshTimer = setInterval(() => handleRefresh(), autoRefreshInterval.value * 1000)
  }
}

const disposeCharts = () => {
  Object.values(chartInstances).forEach(c => { try { c.dispose() } catch {} })
  Object.keys(chartInstances).forEach(k => delete chartInstances[k])
}

const handleResize = () => {
  Object.values(chartInstances).forEach(c => { try { c.resize() } catch {} })
}

onMounted(() => {
  loadVmList()
  window.addEventListener('resize', handleResize)
})

onBeforeUnmount(() => {
  if (autoRefreshTimer) clearInterval(autoRefreshTimer)
  disposeCharts()
  window.removeEventListener('resize', handleResize)
})

defineExpose({ loadVmList, loadList: loadVmList })
</script>

<style scoped>
.vm-monitor-container { padding: 0; }

.monitor-toolbar {
  background: #f7f8fa;
  border: 1px solid #e8e8e8;
  border-radius: 6px;
  padding: 16px 20px;
  margin-bottom: 20px;
}

.toolbar-row {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}

.toolbar-row + .toolbar-row { margin-top: 12px; }

.toolbar-item {
  display: flex;
  align-items: center;
  gap: 8px;
}

.toolbar-label {
  font-size: 13px;
  color: #606266;
  font-weight: 500;
  white-space: nowrap;
}

.charts-area { margin-top: 8px; }

.chart-section { margin-bottom: 20px; }

.chart-section-title {
  font-size: 14px;
  font-weight: 600;
  color: #1d2129;
  margin: 0 0 8px;
  padding-left: 8px;
  border-left: 3px solid #409eff;
}

.chart-wrapper {
  background: #fff;
  border: 1px solid #e8e8e8;
  border-radius: 6px;
  padding: 12px 16px;
}

.chart-box {
  width: 100%;
  height: 260px;
}
</style>
