<template>
  <div class="container">
    <div class="page-header">
      <div class="page-title">
        <h2>自动感知拓扑图</h2>
        <p class="page-subtitle">自动发现网络设备；数据来自 DC-Control /discovery 与 /ipscans</p>
      </div>
      <div class="page-actions">
        <a-select
          v-model="selectedScanId"
          :style="{ width: '220px', marginRight: '8px' }"
          placeholder="选择扫描任务"
          :loading="scansLoading"
          allow-clear
          @change="onScanChange"
        >
          <a-option v-for="t in scanTasks" :key="t.id" :value="t.id">{{ t.name }} (#{{ t.id }})</a-option>
        </a-select>
        <a-button type="primary" :loading="isScanning" :disabled="!selectedScanId" @click="handleScan">
          <template #icon>
            <icon-refresh v-if="isScanning" class="animate-spin" />
            <icon-play-circle v-else />
          </template>
          {{ isScanning ? '扫描中...' : '启动扫描' }}
        </a-button>
        <a-button :loading="pageLoading" @click="reloadAll">
          <template #icon><icon-refresh /></template>
          刷新数据
        </a-button>
        <a-button @click="goIpScanTasks">IP 扫描任务</a-button>
        <a-button @click="goAssetDevices">设备资产</a-button>
      </div>
    </div>

    <a-card :bordered="false" class="mb-6" title="自动感知 · 主扫描范围">
      <p class="config-hint">
        仅将所选扫描任务纳入「当前视图」统计与下方设备列表（对应 DC-Control PUT /discovery/config）。
      </p>
      <div class="primary-scan-row">
        <a-select
          v-model="primaryScanIds"
          multiple
          allow-search
          allow-clear
          placeholder="选择主扫描任务（可多选）"
          :loading="scansLoading"
          :max-tag-count="4"
          class="primary-scan-select"
        >
          <a-option v-for="t in scanTasks" :key="t.id" :value="t.id">{{ t.name }} (#{{ t.id }})</a-option>
        </a-select>
        <a-button type="primary" :loading="savingDiscovery" @click="savePrimaryScanConfig">保存配置</a-button>
      </div>
    </a-card>

    <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-apps /></div>
            <div class="stats-info">
              <div class="stats-title">发现设备（当前视图）</div>
              <div class="stats-value">{{ summary?.device_total_current ?? '—' }}</div>
              <div class="stats-desc text-muted">每 IP 取最新一条</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-success"><icon-wifi /></div>
            <div class="stats-info">
              <div class="stats-title">在线设备</div>
              <div class="stats-value">{{ summary?.online_current ?? '—' }}</div>
              <div class="stats-desc">
                离线 {{ summary?.offline_current ?? '—' }} / 其它 {{ summary?.other_status_current ?? '—' }}
              </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-warning"><icon-exclamation-circle /></div>
            <div class="stats-info">
              <div class="stats-title">上次运行新增</div>
              <div class="stats-value">{{ summary?.last_run?.new_count ?? '—' }}</div>
              <div class="stats-desc text-muted">来自最近一次 scan_run</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-default"><icon-clock-circle /></div>
            <div class="stats-info">
              <div class="stats-title">上次扫描</div>
              <div class="stats-value">{{ lastScanTimeShort }}</div>
              <div class="stats-desc text-muted">{{ lastScanTimeRel }}</div>
            </div>
          </div>
        </a-card>
      </a-col>
    </a-row>

    <a-row :gutter="16" class="mt-6 scan-row">
      <a-col :xs="24" :lg="16" class="scan-col">
        <a-card :bordered="false" class="scan-card">
          <template #title>
            <div class="card-title">
              <span>扫描进度 / 最近一次运行</span>
              <a-tag v-if="runInProgress" color="orange" bordered>进行中</a-tag>
              <a-tag v-else-if="summary?.last_run?.status === 'success'" color="green" bordered>已完成</a-tag>
              <a-tag v-else-if="summary?.last_run?.status === 'failed'" color="red" bordered>失败</a-tag>
            </div>
          </template>
          <div v-if="runInProgress" class="scanning-content">
            <div class="flex items-center gap-4 mb-4">
              <icon-refresh class="h-5 w-5 animate-spin text-primary" />
              <div class="flex-1">
                <p class="text-sm font-medium">扫描任务执行中…</p>
                <p class="text-xs text-muted">任务 #{{ selectedScanId }}；请等待 Agent 回调写入结果</p>
              </div>
            </div>
          </div>
          <div v-else-if="summary?.last_run" class="scan-complete-content">
            <div class="flex items-center gap-4 mb-4">
              <icon-check-circle class="h-5 w-5 text-success" />
              <div class="flex-1">
                <p class="text-sm font-medium">最近运行：{{ summary.last_run.status }}</p>
                <p class="text-xs text-muted">
                  {{ formatRange(summary.last_run.started_at, summary.last_run.finished_at) }}
                </p>
              </div>
            </div>
            <a-row :gutter="16" class="stats-grid">
              <a-col :span="6" class="text-center">
                <div class="text-lg font-semibold">{{ summary.last_run.device_total }}</div>
                <div class="text-xs text-muted">设备数</div>
              </a-col>
              <a-col :span="6" class="text-center">
                <div class="text-lg font-semibold text-success">{{ summary.last_run.online_count }}</div>
                <div class="text-xs text-muted">在线</div>
              </a-col>
              <a-col :span="6" class="text-center">
                <div class="text-lg font-semibold text-muted">{{ summary.last_run.offline_count }}</div>
                <div class="text-xs text-muted">离线</div>
              </a-col>
              <a-col :span="6" class="text-center">
                <div class="text-lg font-semibold text-warning">{{ summary.last_run.new_count }}</div>
                <div class="text-xs text-muted">新增</div>
              </a-col>
            </a-row>
          </div>
          <div v-else class="scan-complete-content">
            <p class="text-sm text-muted">暂无扫描运行记录。请选择任务并点击「启动扫描」。</p>
          </div>
        </a-card>
      </a-col>
      <a-col :xs="24" :lg="8" class="scan-col">
        <a-card :bordered="false" title="扫描配置（当前任务）" class="scan-card">
          <div v-if="activeTask" class="config-section">
            <div class="config-item">
              <p class="config-label">扫描网段</p>
              <div class="config-value-list">
                <a-tag color="arcoblue" bordered>{{ activeTask.target_range || '—' }}</a-tag>
              </div>
            </div>
            <div class="config-item">
              <p class="config-label">任务类型 / 状态</p>
              <div class="config-tags">
                <a-tag bordered>{{ activeTask.type }}</a-tag>
                <a-tag :color="activeTask.last_scan_status === 'running' ? 'orange' : 'arcoblue'" bordered>
                  {{ activeTask.last_scan_status || '—' }}
                </a-tag>
              </div>
            </div>
            <div class="config-item">
              <p class="config-label">Cron（定期）</p>
              <span class="text-sm">{{ activeTask.cron_expr || '未配置' }}</span>
            </div>
          </div>
          <div v-else class="text-sm text-muted">请选择扫描任务</div>
        </a-card>
      </a-col>
    </a-row>

    <a-card class="mt-6" :bordered="false" title="扫描历史">
      <a-table
        :data="scanHistoryRows"
        :columns="historyColumns"
        :pagination="historyPagination"
        row-key="id"
        @page-change="onHistoryPageChange"
      >
        <template #started="{ record }">
          {{ formatTime(record.started_at) }}
        </template>
        <template #newDevices="{ record }">
          <span v-if="record.new_count > 0" class="text-warning">+{{ record.new_count }}</span>
          <span v-else class="text-muted">0</span>
        </template>
        <template #status="{ record }">
          <a-tag :color="record.status === 'success' ? 'green' : record.status === 'running' ? 'orange' : 'red'" bordered>
            {{ record.status }}
          </a-tag>
        </template>
        <template #duration="{ record }">{{ formatDuration(record.started_at, record.finished_at) }}</template>
      </a-table>
    </a-card>

    <a-card class="mt-6" :bordered="false">
      <template #title>
        <div class="table-header">
          <span>发现的设备（当前视图）</span>
          <a-space>
            <a-select v-model="deviceStatusFilter" placeholder="状态" style="width: 120px" allow-clear @change="reloadDevices">
              <a-option value="online">在线</a-option>
              <a-option value="offline">离线</a-option>
              <a-option value="unknown">未知</a-option>
            </a-select>
          </a-space>
        </div>
      </template>
      <a-table
        :data="discoveredDevices"
        :columns="deviceColumns"
        :pagination="devicePagination"
        row-key="id"
        :loading="devicesLoading"
        @page-change="onDevicePageChange"
      >
        <template #status="{ record }">
          <a-tag v-if="record.status === 'online'" color="green" bordered>在线</a-tag>
          <a-tag v-else-if="record.status === 'offline'" color="red" bordered>离线</a-tag>
          <a-tag v-else color="orange" bordered>{{ record.status || '未知' }}</a-tag>
        </template>
        <template #method="{ record }">{{ formatMethods(record.discovery_methods) }}</template>
        <template #lastSeen="{ record }">{{ formatTime(record.last_seen_time) }}</template>
      </a-table>
    </a-card>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn'
import {
  IconRefresh,
  IconCheckCircle,
  IconWifi,
  IconExclamationCircle,
  IconPlayCircle,
  IconApps,
  IconClockCircle,
} from '@arco-design/web-vue/es/icon'
import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
import {
  fetchDiscoverySummary,
  fetchDiscoveryDevices,
  fetchDiscoveryScanRuns,
  updateDiscoveryConfig,
  type DiscoverySummary,
  type DiscoveryDeviceRow,
  type DiscoveryScanRun,
} from '@/api/ops/discovery'
import { fetchIpScanList, fetchIpScanDetail, triggerIpScan, type IpScanTask } from '@/api/ops/ipScan'

dayjs.extend(relativeTime)
dayjs.locale('zh-cn')

const router = useRouter()

const pageLoading = ref(false)
const savingDiscovery = ref(false)
const primaryScanIds = ref<number[]>([])
const scansLoading = ref(false)
const devicesLoading = ref(false)
const isScanning = ref(false)
const scanTasks = ref<IpScanTask[]>([])
const selectedScanId = ref<number | undefined>(undefined)
const activeTask = ref<IpScanTask | null>(null)
const summary = ref<DiscoverySummary | null>(null)
const scanHistoryRows = ref<DiscoveryScanRun[]>([])
const discoveredDevices = ref<DiscoveryDeviceRow[]>([])
const deviceStatusFilter = ref('')

const historyPagination = ref({ current: 1, pageSize: 10, total: 0 })
const devicePagination = ref({ current: 1, pageSize: 20, total: 0 })

let pollTimer: ReturnType<typeof setInterval> | null = null

const runInProgress = computed(() => {
  if (summary.value?.last_run?.status === 'running') return true
  if (activeTask.value?.last_scan_status === 'running') return true
  return false
})

const lastScanTimeShort = computed(() => {
  const t = summary.value?.last_run?.finished_at || summary.value?.last_run?.started_at
  return t ? dayjs(t).format('HH:mm') : '—'
})

const lastScanTimeRel = computed(() => {
  const t = summary.value?.last_run?.finished_at || summary.value?.last_run?.started_at
  return t ? dayjs(t).fromNow() : ''
})

/** 统一解包 bsm-sdk 风格响应 */
function unwrap<T>(res: any): T | undefined {
  if (!res || res.code !== 0) return undefined
  return (res.details ?? res.data) as T
}

const loadScans = async () => {
  scansLoading.value = true
  try {
    const res = await fetchIpScanList({ page: 1, size: 200 })
    const page = unwrap<{ total: number; data: IpScanTask[] }>(res)
    scanTasks.value = page?.data || []
    if (!selectedScanId.value && scanTasks.value.length) {
      selectedScanId.value = scanTasks.value[0].id
      await loadTaskDetail()
    }
  } finally {
    scansLoading.value = false
  }
}

const loadTaskDetail = async () => {
  if (!selectedScanId.value) {
    activeTask.value = null
    return
  }
  const res = await fetchIpScanDetail(selectedScanId.value)
  const t = unwrap<IpScanTask>(res)
  activeTask.value = t || null
}

const syncPrimaryScanFromSummary = () => {
  const ids = summary.value?.primary_scan_ids
  primaryScanIds.value = Array.isArray(ids) ? [...ids] : []
}

const loadSummary = async () => {
  const res = await fetchDiscoverySummary()
  summary.value = unwrap<DiscoverySummary>(res) || null
  syncPrimaryScanFromSummary()
}

const savePrimaryScanConfig = async () => {
  savingDiscovery.value = true
  try {
    const res: any = await updateDiscoveryConfig({ primary_scan_ids: [...primaryScanIds.value] })
    if (res?.code === 0) {
      Message.success('主扫描配置已保存')
      await Promise.all([loadSummary(), loadDevices()])
    } else {
      Message.error(res?.message || '保存失败')
    }
  } catch (e) {
    console.error(e)
    Message.error('保存请求失败')
  } finally {
    savingDiscovery.value = false
  }
}

const goIpScanTasks = () => {
  router.push({ path: '/netarch/ip-scan-tasks' })
}

const goAssetDevices = () => {
  router.push({ path: '/assets/device' })
}

const loadHistory = async () => {
  const res = await fetchDiscoveryScanRuns({
    scan_id: selectedScanId.value,
    page: historyPagination.value.current,
    size: historyPagination.value.pageSize,
  })
  const d = unwrap<{ total: number; data: DiscoveryScanRun[] }>(res)
  scanHistoryRows.value = d?.data || []
  historyPagination.value.total = d?.total || 0
}

const loadDevices = async () => {
  devicesLoading.value = true
  try {
    const res = await fetchDiscoveryDevices({
      scope: 'current',
      page: devicePagination.value.current,
      size: devicePagination.value.pageSize,
      status: deviceStatusFilter.value || undefined,
    })
    const d = unwrap<{ total: number; data: DiscoveryDeviceRow[] }>(res)
    discoveredDevices.value = d?.data || []
    devicePagination.value.total = d?.total || 0
  } finally {
    devicesLoading.value = false
  }
}

const reloadAll = async () => {
  pageLoading.value = true
  try {
    await Promise.all([loadSummary(), loadTaskDetail(), loadHistory(), loadDevices()])
  } finally {
    pageLoading.value = false
  }
}

const reloadDevices = () => {
  devicePagination.value.current = 1
  loadDevices()
}

const onHistoryPageChange = (current: number) => {
  historyPagination.value.current = current
  loadHistory()
}

const onDevicePageChange = (current: number) => {
  devicePagination.value.current = current
  loadDevices()
}

const onScanChange = () => {
  historyPagination.value.current = 1
  loadTaskDetail()
  loadHistory()
}

const stopPoll = () => {
  if (pollTimer) {
    clearInterval(pollTimer)
    pollTimer = null
  }
}

const startPoll = () => {
  stopPoll()
  pollTimer = setInterval(async () => {
    await loadSummary()
    await loadTaskDetail()
    if (!runInProgress.value) {
      isScanning.value = false
      stopPoll()
      await Promise.all([loadHistory(), loadDevices()])
    }
  }, 3000)
}

const handleScan = async () => {
  if (!selectedScanId.value) {
    Message.warning('请先选择扫描任务')
    return
  }
  try {
    const res = await triggerIpScan(selectedScanId.value)
    if (res?.code === 0) {
      Message.success('已触发扫描')
      isScanning.value = true
      await loadSummary()
      await loadTaskDetail()
      startPoll()
    } else {
      Message.error((res as any)?.message || '触发失败')
    }
  } catch (e) {
    console.error(e)
    Message.error('触发请求失败')
  }
}

const formatRange = (a?: string, b?: string) => {
  if (!a) return ''
  const sa = dayjs(a).format('YYYY-MM-DD HH:mm:ss')
  const sb = b ? dayjs(b).format('YYYY-MM-DD HH:mm:ss') : '进行中'
  return `${sa} — ${sb}`
}

const formatDuration = (a?: string, b?: string) => {
  if (!a || !b) return '—'
  const sec = dayjs(b).diff(dayjs(a), 'second')
  if (sec < 60) return `${sec} 秒`
  return `${Math.floor(sec / 60)} 分`
}

const formatTime = (t?: string) => (t ? dayjs(t).format('YYYY-MM-DD HH:mm') : '—')

const formatMethods = (raw?: string) => {
  if (!raw) return '—'
  try {
    const j = JSON.parse(raw)
    if (Array.isArray(j)) return j.join(', ')
    return raw
  } catch {
    return raw
  }
}

const historyColumns: TableColumnData[] = [
  { title: '开始时间', slotName: 'started', width: 180 },
  { title: '类型', dataIndex: 'run_type', width: 100 },
  { title: '耗时', slotName: 'duration', width: 100 },
  { title: '设备数', dataIndex: 'device_total', width: 90, align: 'center' },
  { title: '新增', slotName: 'newDevices', width: 80, align: 'center' },
  { title: '状态', slotName: 'status', width: 100, align: 'center' },
]

const deviceColumns: TableColumnData[] = [
  { title: 'IP', dataIndex: 'ip_address', width: 130 },
  { title: 'MAC', dataIndex: 'mac_address', width: 150 },
  { title: '主机名', dataIndex: 'hostname', width: 140 },
  { title: '厂商', dataIndex: 'manufacturer', width: 140 },
  { title: '类型', dataIndex: 'device_type', width: 100 },
  { title: '状态', dataIndex: 'status', slotName: 'status', width: 100, align: 'center' },
  { title: '最后发现', slotName: 'lastSeen', width: 160 },
  { title: '发现方式', slotName: 'method', width: 120 },
]

onMounted(async () => {
  await loadScans()
  await reloadAll()
})

onUnmounted(() => {
  stopPoll()
})
</script>

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

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

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 24px;

  .page-title {
    h2 {
      margin: 0;
      font-size: 20px;
      font-weight: 500;
      color: var(--color-text-1);
    }

    .page-subtitle {
      margin: 8px 0 0;
      font-size: 14px;
      color: var(--color-text-3);
    }
  }

  .page-actions {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
  }
}

.stats-row {
  margin-bottom: 0;
}

.stats-card {
  .stats-content {
    display: flex;
    align-items: center;
    gap: 16px;
  }

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

    &-primary {
      background: rgba(22, 93, 255, 0.1);
      color: #165dff;
    }

    &-success {
      background: rgba(0, 180, 42, 0.1);
      color: #00b42a;
    }

    &-warning {
      background: rgba(255, 125, 0, 0.1);
      color: #ff7d00;
    }

    &-default {
      background: rgba(134, 144, 156, 0.1);
      color: #86909c;
    }
  }

  .stats-info {
    flex: 1;
  }

  .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;
  }

  .stats-desc {
    font-size: 12px;
    margin-top: 4px;
    display: flex;
    align-items: center;
    gap: 4px;

    &.text-success {
      color: #00b42a;
    }

    &.text-muted {
      color: var(--color-text-3);
    }
  }
}

.mt-6 {
  margin-top: 24px;
}

.mb-6 {
  margin-bottom: 24px;
}

.config-hint {
  margin: 0 0 12px;
  font-size: 13px;
  color: var(--color-text-3);
}

.primary-scan-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
}

.primary-scan-select {
  min-width: 320px;
  flex: 1;
  max-width: 560px;
}

.scan-row {
  display: flex;
  align-items: stretch;
}

.scan-col {
  display: flex;

  :deep(.arco-card) {
    flex: 1;
    display: flex;
    flex-direction: column;

    .arco-card-body {
      flex: 1;
      display: flex;
      flex-direction: column;
    }
  }
}

.scan-card {
  height: 100%;

  :deep(.arco-card-body) {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

.card-title {
  display: flex;
  align-items: center;
  gap: 8px;
}

.scanning-content,
.scan-complete-content {
  padding: 8px 0;
}

.stats-grid {
  background: var(--color-fill-2);
  border-radius: 8px;
  padding: 16px;
}

.config-section {
  .config-item {
    margin-bottom: 16px;

    &:last-child {
      margin-bottom: 0;
    }
  }

  .config-label {
    font-size: 12px;
    color: var(--color-text-3);
    margin-bottom: 8px;
  }

  .config-value-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
  }

  .config-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }
}

.table-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.text-primary {
  color: #165dff;
}

.text-success {
  color: #00b42a;
}

.text-warning {
  color: #ff7d00;
}

.text-muted {
  color: var(--color-text-3);
}

.flex {
  display: flex;
}

.items-center {
  align-items: center;
}

.gap-4 {
  gap: 16px;
}

.flex-1 {
  flex: 1;
}

.h-5 {
  height: 20px;
}

.w-5 {
  width: 20px;
}

.font-medium {
  font-weight: 500;
}

.font-semibold {
  font-weight: 600;
}

.text-sm {
  font-size: 14px;
}

.text-xs {
  font-size: 12px;
}

.text-lg {
  font-size: 18px;
}

.text-center {
  text-align: center;
}

.mb-4 {
  margin-bottom: 16px;
}

.animate-spin {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>
