<template>
  <div class="container">
    <div class="page-toolbar">
      <div class="toolbar-left">
        <span class="toolbar-label">机房</span>
        <a-select
          v-model="selectedRoomId"
          class="room-select"
          placeholder="请选择机房"
          allow-clear
          :loading="roomOptionsLoading"
          @change="onRoomChange"
        >
          <a-option v-for="r in roomOptions" :key="r.id" :value="r.id" :label="r.name">
            {{ r.name }}
            <span v-if="r.code" class="text-muted room-option-code">{{ r.code }}</span>
          </a-option>
        </a-select>
        <a-button type="outline" :disabled="!selectedRoomId" :loading="loading" @click="reloadRoomData">刷新</a-button>
      </div>
      <div v-if="statsPartialHint" class="toolbar-meta text-muted">{{ statsPartialHint }}</div>
    </div>

    <div v-if="!selectedRoomId" class="global-hint text-muted">请选择机房以查看该机房下的动环设备与指标。</div>

    <a-spin :loading="loading" style="width: 100%">
      <!-- 首行：4 张汇总 -->
      <a-row v-if="selectedRoomId" :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">
                <IconBuildingCommunity :size="24" stroke="1.5" />
              </div>
              <div class="stats-info">
                <div class="stats-title">设备数量</div>
                <div class="stats-value">{{ deviceTotal }}</div>
                <div class="stats-desc">已注册采集服务</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">
                <IconCircleCheck :size="24" stroke="1.5" />
              </div>
              <div class="stats-info">
                <div class="stats-title">在线设备</div>
                <div class="stats-value">{{ summaryOnline }}</div>
                <div class="stats-desc">status = online</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">
                <IconActivity :size="24" stroke="1.5" />
              </div>
              <div class="stats-info">
                <div class="stats-title">采集已开启</div>
                <div class="stats-value">{{ summaryCollectOn }}</div>
                <div class="stats-desc">已启用且周期采集开启</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">
                <IconAlertCircle :size="24" stroke="1.5" />
              </div>
              <div class="stats-info">
                <div class="stats-title">离线/未知</div>
                <div class="stats-value">{{ summaryOfflineUnknown }}</div>
                <div class="stats-desc">status 非 online</div>
              </div>
            </div>
          </a-card>
        </a-col>
      </a-row>

      <!-- 第二行：设备卡片 -->
      <div v-if="selectedRoomId && deviceCardsHint" class="device-cards-hint text-muted">{{ deviceCardsHint }}</div>
      <div v-if="selectedRoomId" class="device-cards-row">
        <div v-for="d in devicesForCards" :key="d.id" class="device-card-wrapper">
          <a-card class="device-card" :bordered="false" hoverable>
            <template #title>
              <div class="device-card-header">
                <span class="device-card-title">{{ d.name }}</span>
                <a-tag :color="getStatusColor(d.status)" size="small">{{ formatStatusText(d.status) }}</a-tag>
              </div>
            </template>
            <div class="device-card-body">
              <a-descriptions
                :column="1"
                size="small"
                :value-style="{ width: '100%', color: 'var(--color-text-2)' }"
                :label-style="{ color: 'var(--color-text-3)', fontWeight: 500 }"
              >
                <a-descriptions-item label="分类">{{ DEVICE_CATEGORY_MAP[d.device_category] || d.device_category }}</a-descriptions-item>
                <a-descriptions-item label="型号">{{ d.type || '-' }}</a-descriptions-item>
                <a-descriptions-item label="采集">
                  <a-tag :color="d.collect_method === 'snmp' ? 'purple' : 'arcoblue'" size="small">
                    {{ d.collect_method === 'snmp' ? 'SNMP' : 'API' }}
                  </a-tag>
                </a-descriptions-item>
                <a-descriptions-item label="最近检查">{{ formatDateTime(d.last_check_time) }}</a-descriptions-item>
              </a-descriptions>
              <div v-if="d.collect_last_result" class="device-card-result" :title="d.collect_last_result">
                <a-typography-text :ellipsis="{ rows: 2, showTooltip: true }" class="text-muted">
                  {{ d.collect_last_result }}
                </a-typography-text>
              </div>
              <a-divider :margin="12" />
              <div class="device-metrics">
                <a-spin :loading="metricsLoading && isMetricsTarget(d)" :size="16">
                  <a-descriptions
                    bordered
                    :column="2"
                    size="small"
                    :label-style="{ color: 'var(--color-text-3)' }"
                    :value-style="{ color: 'var(--color-text-1)', fontWeight: 600, fontFamily: 'SF Mono, Menlo, Monaco, monospace' }"
                  >
                    <template v-if="metricsForDevice(d).length">
                      <a-descriptions-item
                        v-for="(m, idx) in metricsForDevice(d)"
                        :key="`${m.metric_name}-${idx}`"
                        :label="m.metric_name"
                        :span="1"
                      >
                        {{ m.metric_value }}
                        <span v-if="m.metric_unit" class="metric-unit">({{ m.metric_unit }})</span>
                      </a-descriptions-item>
                    </template>
                    <template v-else-if="!isMetricsTarget(d)">
                      <a-descriptions-item :span="2" class="text-muted">指标见前 {{ METRICS_CARD_LIMIT }} 台</a-descriptions-item>
                    </template>
                    <template v-else>
                      <a-descriptions-item :span="2" class="text-muted">暂无指标</a-descriptions-item>
                    </template>
                  </a-descriptions>
                </a-spin>
              </div>
            </div>
          </a-card>
        </div>
      </div>
      <a-empty v-if="selectedRoomId && !devicesAll.length && !loading" description="该机房下暂无设备" />

      <!-- 设备列表 -->
      <a-card v-if="selectedRoomId" class="table-card" title="设备列表" :bordered="false">
        <template #extra>
          <a-select v-model="selectedCategory" placeholder="全部分类" allow-clear style="width: 160px" :options="DEVICE_CATEGORY_OPTIONS" />
        </template>
        <a-table
          :data="tableRowsPaged"
          :columns="columns"
          :loading="loading"
          :pagination="tablePagination"
          row-key="id"
          @page-change="onTablePageChange"
          @page-size-change="onTablePageSizeChange"
        >
          <template #device_category="{ record }">
            {{ DEVICE_CATEGORY_MAP[record.device_category] || record.device_category || '-' }}
          </template>
          <template #collect_method="{ record }">
            <a-tag :color="record.collect_method === 'snmp' ? 'purple' : 'arcoblue'" size="small">
              {{ record.collect_method === 'snmp' ? 'SNMP' : 'API' }}
            </a-tag>
          </template>
          <template #enabled_collect="{ record }">
            <a-space>
              <a-tag :color="record.enabled ? 'green' : 'gray'" size="small">
                {{ record.enabled ? '启用' : '禁用' }}
              </a-tag>
              <a-tag :color="record.collect_on ? 'green' : 'gray'" size="small">
                {{ record.collect_on ? '周期采' : '未周期' }}
              </a-tag>
            </a-space>
          </template>
          <template #status="{ record }">
            <a-tag :color="getStatusColor(record.status)" bordered>
              {{ formatStatusText(record.status) }}
            </a-tag>
          </template>
          <template #collect_last_result="{ record }">
            <a-typography-text :ellipsis="{ rows: 2, showTooltip: true }">
              {{ record.collect_last_result || '-' }}
            </a-typography-text>
          </template>
          <template #response_time="{ record }">
            {{ record.response_time != null && record.response_time !== undefined ? Number(record.response_time).toFixed(1) : '-' }}
          </template>
          <template #last_check_time="{ record }">
            {{ formatDateTime(record.last_check_time) }}
          </template>
          <template #continuous_errors="{ record }">
            {{ record.continuous_errors != null ? record.continuous_errors : '-' }}
          </template>
        </a-table>
      </a-card>
    </a-spin>
  </div>
</template>

<script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue'
import { Message } from '@arco-design/web-vue'
import { IconBuildingCommunity, IconCircleCheck, IconActivity, IconAlertCircle } from '@tabler/icons-vue'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
import { fetchRoomOptions, type RoomOptionItem } from '@/api/ops/room'
import {
  fetchRoomDeviceList,
  fetchLatestMetrics,
  DEVICE_CATEGORY_MAP,
  DEVICE_CATEGORY_OPTIONS,
  type RoomDeviceItem,
  type MetricItem,
} from '@/api/ops/room-device'

const METRICS_CARD_LIMIT = 8
const LIST_PAGE_SIZE = 100
const METRICS_CONCURRENCY = 6

const roomOptionsLoading = ref(false)
const roomOptions = ref<RoomOptionItem[]>([])
const selectedRoomId = ref<number | undefined>(undefined)

const loading = ref(false)
const devicesAll = ref<RoomDeviceItem[]>([])
const deviceTotal = ref(0)
const selectedCategory = ref<string | undefined>(undefined)

const metricsByIdentity = ref<Record<string, MetricItem[]>>({})
const metricsLoading = ref(false)

const tablePagination = ref({
  current: 1,
  pageSize: 20,
  total: 0,
  showTotal: true,
  showPageSize: true,
})

const columns: TableColumnData[] = [
  { title: '名称', dataIndex: 'name', minWidth: 180, ellipsis: true, tooltip: true },
  { title: '设备分类', dataIndex: 'device_category', slotName: 'device_category', minWidth: 100 },
  { title: '类型/型号', dataIndex: 'type', minWidth: 120, ellipsis: true, tooltip: true },
  { title: '采集方式', dataIndex: 'collect_method', slotName: 'collect_method', minWidth: 96 },
  { title: '启用 / 周期采集', dataIndex: 'enabled', slotName: 'enabled_collect', minWidth: 160 },
  { title: '状态', dataIndex: 'status', slotName: 'status', minWidth: 100, align: 'center' },
  { title: '最近检查', dataIndex: 'last_check_time', slotName: 'last_check_time', minWidth: 250, ellipsis: true, tooltip: true },
  { title: '响应(ms)', dataIndex: 'response_time', slotName: 'response_time', minWidth: 88 },
  { title: '连续错误', dataIndex: 'continuous_errors', slotName: 'continuous_errors', minWidth: 88 },
  { title: '采集摘要', dataIndex: 'collect_last_result', slotName: 'collect_last_result', minWidth: 200 },
]

const statsPartialHint = computed(() => {
  if (!selectedRoomId.value || !deviceTotal.value) return ''
  if (deviceTotal.value > devicesAll.value.length) {
    return `离线等汇总基于已加载的 ${devicesAll.value.length} 条，设备总数为 ${deviceTotal.value}。`
  }
  return ''
})

const summaryOnline = computed(() => devicesAll.value.filter((d) => d.status === 'online').length)
const summaryCollectOn = computed(() => devicesAll.value.filter((d) => d.enabled && d.collect_on).length)
const summaryOfflineUnknown = computed(() => devicesAll.value.filter((d) => d.status !== 'online').length)

const devicesForCards = computed(() => devicesAll.value)

const deviceCardsHint = computed(() => {
  if (!devicesAll.value.length) return ''
  const n = devicesAll.value.length
  if (n > METRICS_CARD_LIMIT) {
    return `以下为 ${n} 台设备卡片；最新指标仅拉取前 ${METRICS_CARD_LIMIT} 台，其余请见列表或详情。`
  }
  return ''
})

const filteredTableDevices = computed(() => {
  const cat = selectedCategory.value
  if (!cat) return devicesAll.value
  return devicesAll.value.filter((d) => d.device_category === cat)
})

const tableRowsPaged = computed(() => {
  const list = filteredTableDevices.value
  const { current, pageSize } = tablePagination.value
  const start = (current - 1) * pageSize
  return list.slice(start, start + pageSize)
})

watch(filteredTableDevices, (list) => {
  tablePagination.value.total = list.length
  const maxPage = Math.max(1, Math.ceil(list.length / tablePagination.value.pageSize) || 1)
  if (tablePagination.value.current > maxPage) {
    tablePagination.value.current = 1
  }
})

watch(selectedCategory, () => {
  tablePagination.value.current = 1
})

function onTablePageChange(current: number) {
  tablePagination.value.current = current
}

function onTablePageSizeChange(size: number) {
  tablePagination.value.pageSize = size
  tablePagination.value.current = 1
}

function truncate(s: string, max: number) {
  if (!s || s.length <= max) return s
  return `${s.slice(0, max)}…`
}

function formatDateTime(v?: string) {
  if (!v) return '-'
  const d = new Date(v)
  if (Number.isNaN(d.getTime())) return v
  const p = (n: number) => String(n).padStart(2, '0')
  return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
}

function formatStatusText(status?: string) {
  const m: Record<string, string> = {
    online: '在线',
    offline: '离线',
    unknown: '未知',
  }
  return m[status || ''] || status || '-'
}

function getStatusColor(status?: string) {
  const colorMap: Record<string, string> = {
    online: 'green',
    offline: 'red',
    unknown: 'gray',
    warning: 'orange',
    success: 'green',
  }
  return colorMap[status || ''] || 'gray'
}

function isMetricsTarget(d: RoomDeviceItem) {
  const targets = devicesAll.value.slice(0, METRICS_CARD_LIMIT).map((x) => x.service_identity)
  return targets.includes(d.service_identity)
}

function metricsForDevice(d: RoomDeviceItem): MetricItem[] {
  return metricsByIdentity.value[d.service_identity] || []
}

async function loadRoomOptions() {
  roomOptionsLoading.value = true
  try {
    const res: any = await fetchRoomOptions()
    let list: RoomOptionItem[] = []
    if (Array.isArray(res)) {
      list = res
    } else if (res?.details) {
      list = Array.isArray(res.details) ? res.details : res.details.data || []
    } else {
      list = res?.data ?? []
    }
    roomOptions.value = Array.isArray(list) ? list : []
    if (selectedRoomId.value === undefined && roomOptions.value.length > 0) {
      selectedRoomId.value = roomOptions.value[0].id
    }
  } catch (e) {
    console.error(e)
    Message.error('加载机房列表失败')
    roomOptions.value = []
  } finally {
    roomOptionsLoading.value = false
  }
}

async function loadRoomDevices() {
  if (!selectedRoomId.value) {
    devicesAll.value = []
    deviceTotal.value = 0
    metricsByIdentity.value = {}
    return
  }
  loading.value = true
  metricsByIdentity.value = {}
  try {
    const res: any = await fetchRoomDeviceList({
      room_id: String(selectedRoomId.value),
      page: 1,
      size: LIST_PAGE_SIZE,
    })
    const page = res?.details ?? res
    const list: RoomDeviceItem[] = page?.data ?? []
    devicesAll.value = list
    deviceTotal.value = page?.total ?? list.length
    if (res?.code !== undefined && res.code !== 0) {
      Message.error(res.message || '加载机房设备失败')
    }
  } catch (e) {
    console.error(e)
    Message.error('加载机房设备失败')
    devicesAll.value = []
    deviceTotal.value = 0
  } finally {
    loading.value = false
  }
}

async function loadMetricsForFirstDevices() {
  const slice = devicesAll.value.slice(0, METRICS_CARD_LIMIT)
  if (!slice.length) return
  metricsLoading.value = true
  try {
    for (let i = 0; i < slice.length; i += METRICS_CONCURRENCY) {
      const batch = slice.slice(i, i + METRICS_CONCURRENCY)
      await Promise.all(
        batch.map(async (d) => {
          try {
            const res = await fetchLatestMetrics(d.service_identity)
            const metrics = res?.code === 0 && res.details?.metrics && Array.isArray(res.details.metrics) ? res.details.metrics : []
            metricsByIdentity.value = { ...metricsByIdentity.value, [d.service_identity]: metrics }
          } catch {
            metricsByIdentity.value = { ...metricsByIdentity.value, [d.service_identity]: [] }
          }
        })
      )
    }
  } finally {
    metricsLoading.value = false
  }
}

async function reloadRoomData() {
  await loadRoomDevices()
  await loadMetricsForFirstDevices()
}

function onRoomChange() {
  selectedCategory.value = undefined
  tablePagination.value.current = 1
  metricsByIdentity.value = {}
  void reloadRoomData()
}

onMounted(async () => {
  await loadRoomOptions()
  if (selectedRoomId.value) {
    await reloadRoomData()
  }
})
</script>

<script lang="ts">
export default {
  name: 'EnvironmentMonitor',
}
</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: 12px;
}

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

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

.room-select {
  min-width: 220px;
  max-width: 360px;
}

.room-option-code {
  margin-left: 8px;
  font-size: 12px;
}

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

.global-hint {
  margin-bottom: 12px;
  font-size: 13px;
}

.device-cards-hint {
  margin-bottom: 8px;
  font-size: 12px;
}

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

.device-cards-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 16px;
}

.device-card-wrapper {
  flex: 1 1 calc(25% - 12px);
  min-width: 280px;
  max-width: 100%;
}

@media (max-width: 1200px) {
  .device-card-wrapper {
    flex: 1 1 calc(33.33% - 11px);
  }
}

@media (max-width: 992px) {
  .device-card-wrapper {
    flex: 1 1 calc(50% - 8px);
  }
}

@media (max-width: 768px) {
  .device-card-wrapper {
    flex: 1 1 100%;
  }
}

.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;
    flex-shrink: 0;

    &-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);
  }
}

.device-card {
  height: 100%;
  border-radius: 12px;
  background: var(--color-bg-2);
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.06),
    0 1px 2px rgba(0, 0, 0, 0.04);
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);

  :deep(.arco-card-header) {
    padding: 16px 16px 12px;
    border-bottom: 1px solid var(--color-border-1);
  }

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

  &:hover {
    box-shadow:
      0 4px 12px rgba(0, 0, 0, 0.08),
      0 2px 4px rgba(0, 0, 0, 0.06);
    transform: translateY(-2px);
  }
}

.device-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.device-card-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--color-text-1);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}

.device-card-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.device-card-result {
  font-size: 12px;
  line-height: 1.5;
  padding: 10px 12px;
  background: var(--color-fill-1);
  border-radius: 8px;
  border-left: 3px solid var(--color-border-2);
}

.device-metrics {
  min-height: 48px;
}

.table-card {
  margin-top: 8px;

  :deep(.arco-card-header) {
    align-items: center;
  }
}

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