<template>
  <div class="request-flow-page">
    <div class="toolbar">
      <el-date-picker
        v-model="dateRange"
        type="datetimerange"
        range-separator="至"
        start-placeholder="开始时间"
        end-placeholder="结束时间"
        value-format="YYYY-MM-DD HH:mm:ss"
        format="YYYY-MM-DD HH:mm:ss"
        class="toolbar-item"
      />
      <el-input-number v-model="topN" :min="10" :max="1000" :step="10" class="toolbar-item" />
      <el-button type="primary" :loading="loading" @click="loadData">刷新请求流</el-button>
      <el-button v-if="selectedIP || selectedPath" @click="clearFilter">清空筛选</el-button>
    </div>

    <div class="content">
      <div ref="chartRef" class="chart"></div>
      <div class="side-list">
        <h4>TOP 可疑来源</h4>
        <div v-if="rows.length === 0" class="empty">暂无数据</div>
        <div
          v-for="(item, index) in rows.slice(0, 20)"
          :key="`${item.ip}-${item.path}-${index}`"
          class="row clickable"
          @click="selectFlow(item)"
        >
          <div class="name">{{ item.from_name }} -> {{ item.path }}</div>
          <div class="value">{{ item.value }}</div>
        </div>
      </div>
    </div>

    <div class="table-card">
      <div class="table-title">
        可疑IP明细
        <span v-if="selectedIP || selectedPath" class="hint">
          （筛选：{{ selectedIP || '-' }} / {{ selectedPath || '-' }}）
        </span>
      </div>
      <el-table :data="listRows" border size="small" height="360">
        <el-table-column prop="ip" label="IP" width="150" />
        <el-table-column prop="path" label="接口路径" min-width="240" />
        <el-table-column prop="method" label="方法" width="90" />
        <el-table-column prop="request_count" label="次数" width="90" />
        <el-table-column prop="trigger_reason" label="触发原因" width="160" />
        <el-table-column prop="created_at" label="时间" width="180" />
      </el-table>
      <div class="pager">
        <el-pagination
          v-model:current-page="page"
          v-model:page-size="pageSize"
          :page-sizes="[10, 20, 50]"
          :total="total"
          layout="total, sizes, prev, pager, next"
          @current-change="loadList"
          @size-change="handlePageSizeChange"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { adminGetSuspiciousIPGeoStream, adminGetSuspiciousIPList } from '@/api/statistics'
import * as echarts from 'echarts'
import { ElMessage } from 'element-plus'
import { nextTick, onMounted, onUnmounted, ref } from 'vue'

const loading = ref(false)
const topN = ref(200)
const dateRange = ref([])
const rows = ref([])
const listRows = ref([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(20)
const selectedIP = ref('')
const selectedPath = ref('')
const chartRef = ref(null)
let chart = null

const getDefaultRange = () => {
  const now = new Date()
  const start = new Date(now.getTime() - 24 * 60 * 60 * 1000)
  const format = (d) => {
    const y = d.getFullYear()
    const m = `${d.getMonth() + 1}`.padStart(2, '0')
    const day = `${d.getDate()}`.padStart(2, '0')
    const h = `${d.getHours()}`.padStart(2, '0')
    const mi = `${d.getMinutes()}`.padStart(2, '0')
    const s = `${d.getSeconds()}`.padStart(2, '0')
    return `${y}-${m}-${day} ${h}:${mi}:${s}`
  }
  return [format(start), format(now)]
}

const renderChart = () => {
  if (!chartRef.value) return
  if (!chart) chart = echarts.init(chartRef.value)

  const lineData = rows.value.map(item => ({
    coords: [
      [item.from_lng, item.from_lat],
      [item.to_lng, item.to_lat]
    ],
    value: item.value
  }))

  const pointData = rows.value.map(item => ({
    name: item.from_name,
    value: [item.from_lng, item.from_lat, item.value]
  }))

  chart.setOption({
    backgroundColor: '#040b1b',
    tooltip: {
      trigger: 'item'
    },
    xAxis: {
      type: 'value',
      min: -180,
      max: 180,
      axisLabel: { color: '#6b7280' },
      splitLine: { lineStyle: { color: 'rgba(255,255,255,0.08)' } }
    },
    yAxis: {
      type: 'value',
      min: -90,
      max: 90,
      axisLabel: { color: '#6b7280' },
      splitLine: { lineStyle: { color: 'rgba(255,255,255,0.08)' } }
    },
    series: [
      {
        name: '请求流',
        type: 'lines',
        coordinateSystem: 'cartesian2d',
        zlevel: 2,
        effect: {
          show: true,
          period: 4,
          symbol: 'arrow',
          symbolSize: 6
        },
        lineStyle: {
          width: 1,
          color: '#4cc9f0',
          curveness: 0.2
        },
        data: lineData
      },
      {
        name: '来源点',
        type: 'scatter',
        coordinateSystem: 'cartesian2d',
        symbolSize: (val) => Math.max(6, Math.min(18, (val[2] || 1) / 2)),
        itemStyle: {
          color: '#f72585'
        },
        data: pointData
      }
    ]
  })
  chart.off('click')
  chart.on('click', (params) => {
    if (params.seriesName === '来源点' && params.dataIndex >= 0) {
      const item = rows.value[params.dataIndex]
      if (item) selectFlow(item)
    }
  })
}

const selectFlow = (item) => {
  selectedIP.value = item.ip || ''
  selectedPath.value = item.path || ''
  page.value = 1
  loadList()
}

const clearFilter = () => {
  selectedIP.value = ''
  selectedPath.value = ''
  page.value = 1
  loadList()
}

const loadList = async () => {
  try {
    if (!dateRange.value || dateRange.value.length !== 2) {
      dateRange.value = getDefaultRange()
    }
    const res = await adminGetSuspiciousIPList({
      page: page.value,
      page_size: pageSize.value,
      start_time: dateRange.value[0],
      end_time: dateRange.value[1],
      ip: selectedIP.value || undefined,
      path: selectedPath.value || undefined
    })
    listRows.value = res.data?.items || []
    total.value = res.data?.total || 0
  } catch (error) {
    console.error('加载可疑IP明细失败:', error)
    ElMessage.error('加载可疑IP明细失败')
  }
}

const handlePageSizeChange = (size) => {
  pageSize.value = size
  page.value = 1
  loadList()
}

const loadData = async () => {
  loading.value = true
  try {
    if (!dateRange.value || dateRange.value.length !== 2) {
      dateRange.value = getDefaultRange()
    }
    const res = await adminGetSuspiciousIPGeoStream({
      start_time: dateRange.value[0],
      end_time: dateRange.value[1],
      top_n: topN.value
    })
    rows.value = res.data || []
    await nextTick()
    renderChart()
    loadList()
  } catch (error) {
    console.error('加载请求流失败:', error)
    ElMessage.error('加载请求流失败')
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  dateRange.value = getDefaultRange()
  loadData()
})

onUnmounted(() => {
  if (chart) {
    chart.dispose()
    chart = null
  }
})
</script>

<style scoped>
.request-flow-page {
  width: 100%;
}
.toolbar {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-bottom: 12px;
}
.toolbar-item {
  min-width: 220px;
}
.content {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: 12px;
}
.chart {
  height: 620px;
  border-radius: 10px;
  overflow: hidden;
}
.side-list {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 12px;
  max-height: 620px;
  overflow-y: auto;
}
.row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 12px;
  padding: 8px 0;
  border-bottom: 1px solid #f3f4f6;
}
.clickable {
  cursor: pointer;
}
.clickable:hover {
  background: #f9fafb;
}
.name {
  color: #374151;
  word-break: break-all;
}
.value {
  color: #111827;
  font-weight: 600;
}
.empty {
  color: #9ca3af;
}
.table-card {
  margin-top: 12px;
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  padding: 12px;
}
.table-title {
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 10px;
}
.hint {
  margin-left: 6px;
  font-size: 12px;
  color: #6b7280;
}
.pager {
  margin-top: 10px;
  display: flex;
  justify-content: flex-end;
}
@media (max-width: 1200px) {
  .content {
    grid-template-columns: 1fr;
  }
}
</style>

