<template>
  <div class="container">
    <search-table
      :form-model="formModel"
      :form-items="formItems"
      :data="tableData"
      :columns="columns"
      :loading="loading"
      :pagination="pagination"
      title="日志查询"
      search-button-text="查询"
      reset-button-text="重置"
      :show-download="false"
      @update:form-model="handleFormModelUpdate"
      @search="handleSearch"
      @reset="handleReset"
      @page-change="handlePageChange"
      @page-size-change="handlePageSizeChange"
      @refresh="handleRefresh"
    >
      <template #toolbar-left>
        <a-space>
          <a-button type="outline" @click="openOutboxDrawer">重放结果</a-button>
          <a-button type="outline" @click="showUnparsedQueue">未解析队列</a-button>
        </a-space>
      </template>
      <template #source_kind="{ record }">
        <a-tag>{{ sourceKindLabel(record.source_kind) }}</a-tag>
      </template>
      <template #raw_payload="{ record }">
        <span class="ellipsis-cell" :title="record.raw_payload">{{ truncate(record.raw_payload, 80) }}</span>
      </template>
      <template #severity_code="{ record }">
        <a-tag :color="severityColor(record.severity_code)">{{ severityLabel(record.severity_code) }}</a-tag>
      </template>
      <template #alert_sent="{ record }">
        <a-tag :color="record.alert_sent ? 'green' : 'gray'">
          {{ record.alert_sent ? '已转发' : '否' }}
        </a-tag>
      </template>
      <template #dispatch_status="{ record }">
        <a-tag :color="outboxStatusColor(record.dispatch_status)">
          {{ outboxStatusLabel(record.dispatch_status) }}
        </a-tag>
      </template>
      <template #operations="{ record }">
        <a-space>
          <a-button type="text" size="small" @click="openDetail(record)">详情</a-button>
          <a-button type="text" size="small" @click="handleReplayLog(record)">重放</a-button>
        </a-space>
      </template>
    </search-table>

    <a-drawer
      v-model:visible="detailVisible"
      width="640px"
      :title="detailRow ? `日志详情 #${detailRow.id}` : '日志详情'"
      :footer="false"
      unmount-on-close
    >
      <template v-if="detailRow">
        <a-descriptions :column="1" bordered size="large">
          <a-descriptions-item label="来源类型">
            {{ sourceKindLabel(detailRow.source_kind) }}
          </a-descriptions-item>
          <a-descriptions-item label="采集时间">{{ detailRow.created_at }}</a-descriptions-item>
          <a-descriptions-item label="来源地址">{{ detailRow.remote_addr || '-' }}</a-descriptions-item>
          <a-descriptions-item label="来源 IP">{{ detailRow.source_ip || '-' }}</a-descriptions-item>
          <a-descriptions-item label="设备名">{{ detailRow.device_name || '-' }}</a-descriptions-item>
          <a-descriptions-item label="资源类型">{{ detailRow.resource_type || '-' }}</a-descriptions-item>
          <a-descriptions-item label="资源 ID">{{ detailRow.resource_id || '-' }}</a-descriptions-item>
          <a-descriptions-item label="资源名称">{{ detailRow.resource_name || '-' }}</a-descriptions-item>
          <a-descriptions-item label="命中方式">{{ detailRow.match_method || '-' }}</a-descriptions-item>
          <a-descriptions-item label="严重级别">{{ severityLabel(detailRow.severity_code) }}</a-descriptions-item>
          <a-descriptions-item label="Trap OID">{{ detailRow.trap_oid || '-' }}</a-descriptions-item>
          <a-descriptions-item label="已转发告警">
            {{ detailRow.alert_sent ? '是' : '否' }}
          </a-descriptions-item>
          <a-descriptions-item label="分发状态">
            {{ outboxStatusLabel(detailRow.dispatch_status) }}
          </a-descriptions-item>
          <a-descriptions-item label="摘要">{{ detailRow.normalized_summary || '-' }}</a-descriptions-item>
          <a-descriptions-item label="详情">
            <pre class="pre-block">{{ detailRow.normalized_detail || '-' }}</pre>
          </a-descriptions-item>
          <a-descriptions-item label="原始报文">
            <pre class="pre-block">{{ detailRow.raw_payload || '-' }}</pre>
          </a-descriptions-item>
        </a-descriptions>
      </template>
    </a-drawer>

    <a-drawer v-model:visible="outboxVisible" width="920px" title="重放结果与告警队列" :footer="false" unmount-on-close>
      <a-space direction="vertical" fill>
        <a-space>
          <a-select v-model="outboxStatus" :style="{ width: '180px' }" placeholder="全部状态" allow-clear @change="handleOutboxSearch">
            <a-option value="">全部状态</a-option>
            <a-option value="pending">pending</a-option>
            <a-option value="retrying">retrying</a-option>
            <a-option value="sent">sent</a-option>
            <a-option value="dead">dead</a-option>
          </a-select>
          <a-button :loading="outboxLoading" @click="fetchOutboxList">刷新</a-button>
        </a-space>
        <a-table :data="outboxRows" :loading="outboxLoading" :pagination="false" row-key="id" size="small">
          <template #columns>
            <a-table-column title="ID" data-index="id" :width="70" />
            <a-table-column title="日志ID" :width="100">
              <template #cell="{ record }">
                <a-button type="text" size="small" @click="jumpToLogDetail(record.log_event_id)">
                  {{ record.log_event_id }}
                </a-button>
              </template>
            </a-table-column>
            <a-table-column title="状态" :width="110">
              <template #cell="{ record }">
                <a-tag :color="outboxStatusColor(record.status)">
                  {{ outboxStatusLabel(record.status) }}
                </a-tag>
              </template>
            </a-table-column>
            <a-table-column title="重试次数" data-index="retry_count" :width="90" />
            <a-table-column title="下次重试" data-index="next_retry_at" :width="180" />
            <a-table-column title="错误" data-index="last_error" ellipsis tooltip />
            <a-table-column title="操作" :width="150">
              <template #cell="{ record }">
                <a-button type="text" size="small" :disabled="record.status === 'sent'" @click="handleOutboxRetry(record.id)">
                  重试
                </a-button>
                <a-button type="text" size="small" @click="openPayloadModal(record.payload_json)">Payload</a-button>
              </template>
            </a-table-column>
          </template>
        </a-table>
        <a-pagination
          :current="outboxPagination.current"
          :total="outboxPagination.total"
          :page-size="outboxPagination.pageSize"
          show-total
          show-page-size
          @change="handleOutboxPageChange"
          @page-size-change="handleOutboxPageSizeChange"
        />
      </a-space>
    </a-drawer>

    <a-modal v-model:visible="payloadVisible" title="Payload 详情" width="760px" :footer="false" unmount-on-close>
      <pre class="pre-block">{{ payloadText }}</pre>
    </a-modal>
  </div>
</template>

<script lang="ts" setup>
import { computed, reactive, ref } from 'vue'
import { Message, Modal } from '@arco-design/web-vue'
import type { FormItem } from '@/components/search-form/types'
import SearchTable from '@/components/search-table/index.vue'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
import { fetchAlertOutbox, fetchLogEntries, replayLogEntry, retryAlertOutbox, unwrapLogsPayload, type AlertOutbox, type LogEvent } from '@/api/ops/logs'
import { fetchSeverityOptions } from '@/api/ops/alertPolicy'

const loading = ref(false)
const tableData = ref<LogEvent[]>([])
const formModel = ref({
  source_kind: '' as string,
  resource_type: '' as string,
  resource_id: '' as string,
  dispatch_status: '' as string,
})
const detailVisible = ref(false)
const detailRow = ref<LogEvent | null>(null)
const outboxVisible = ref(false)
const outboxStatus = ref('')
const outboxLoading = ref(false)
const outboxRows = ref<AlertOutbox[]>([])
const payloadVisible = ref(false)
const payloadText = ref('')
type SeverityOption = {
  code: string
  name?: string
  color?: string
}
const severityOptions = ref<SeverityOption[]>([])
const outboxPagination = reactive({
  current: 1,
  pageSize: 20,
  total: 0,
})

const pagination = reactive({
  current: 1,
  pageSize: 50,
  total: 0,
})

const formItems = computed<FormItem[]>(() => [
  {
    field: 'source_kind',
    label: '来源类型',
    type: 'select',
    placeholder: '全部',
    span: 6,
    options: [
      { label: '全部', value: '' },
      { label: 'Syslog', value: 'syslog' },
      { label: 'SNMP Trap', value: 'snmp_trap' },
    ],
  },
  {
    field: 'resource_type',
    label: '资源类型',
    type: 'select',
    placeholder: '全部',
    span: 6,
    options: [
      { label: '全部', value: '' },
      { label: '服务器', value: 'server' },
      { label: '采集设备', value: 'collector' },
      { label: '设备', value: 'device' },
    ],
  },
  {
    field: 'resource_id',
    label: '资源ID',
    type: 'input',
    placeholder: '输入资源ID',
    span: 6,
  },
  {
    field: 'dispatch_status',
    label: '分发状态',
    type: 'select',
    placeholder: '全部',
    span: 6,
    options: [
      { label: '全部', value: '' },
      { label: '待发送', value: 'pending' },
      { label: '重试中', value: 'retrying' },
      { label: '已发送', value: 'sent' },
      { label: '已失败', value: 'dead' },
      { label: '不适用', value: 'not_applicable' },
    ],
  },
])

const columns = computed<TableColumnData[]>(() => [
  { title: 'ID', dataIndex: 'id', width: 80 },
  {
    title: '来源',
    dataIndex: 'source_kind',
    slotName: 'source_kind',
    width: 110,
  },
  { title: '时间', dataIndex: 'created_at', width: 180, ellipsis: true, tooltip: true },
  { title: '来源地址', dataIndex: 'remote_addr', width: 130, ellipsis: true, tooltip: true },
  { title: '来源IP', dataIndex: 'source_ip', width: 120, ellipsis: true, tooltip: true },
  { title: '设备', dataIndex: 'device_name', width: 120, ellipsis: true, tooltip: true },
  { title: '资源类型', dataIndex: 'resource_type', width: 100 },
  { title: '资源ID', dataIndex: 'resource_id', width: 140, ellipsis: true, tooltip: true },
  { title: '资源名称', dataIndex: 'resource_name', width: 140, ellipsis: true, tooltip: true },
  { title: '命中方式', dataIndex: 'match_method', width: 90 },
  { title: '级别', dataIndex: 'severity_code', slotName: 'severity_code', width: 110 },
  { title: 'OID', dataIndex: 'trap_oid', width: 140, ellipsis: true, tooltip: true },
  { title: '分发状态', dataIndex: 'dispatch_status', slotName: 'dispatch_status', width: 110 },
  {
    title: '原始报文',
    dataIndex: 'raw_payload',
    slotName: 'raw_payload',
    ellipsis: true,
    tooltip: true,
  },
  {
    title: '已告警',
    dataIndex: 'alert_sent',
    slotName: 'alert_sent',
    width: 90,
  },
  {
    title: '操作',
    dataIndex: 'operations',
    slotName: 'operations',
    width: 132,
    fixed: 'right',
  },
])

function sourceKindLabel(k: string) {
  if (k === 'syslog') return 'Syslog'
  if (k === 'snmp_trap') return 'SNMP Trap'
  return k || '-'
}

function outboxStatusLabel(status: string) {
  if (status === 'pending') return '待发送'
  if (status === 'retrying') return '重试中'
  if (status === 'sent') return '已发送'
  if (status === 'dead') return '已失败'
  return status || '-'
}

function outboxStatusColor(status: string) {
  if (status === 'pending') return 'arcoblue'
  if (status === 'retrying') return 'orange'
  if (status === 'sent') return 'green'
  if (status === 'dead') return 'red'
  return 'gray'
}

function severityMeta(code: string) {
  const key = (code || '').trim().toLowerCase()
  if (!key) return null
  return (
    severityOptions.value.find(
      (item) =>
        String(item.code || '')
          .trim()
          .toLowerCase() === key
    ) || null
  )
}

function severityLabel(code: string) {
  const meta = severityMeta(code)
  if (meta?.name) return meta.name
  const key = (code || '').toLowerCase()
  if (!key) return '-'
  if (['critical', 'fatal', 'emergency', 'emerg', 'alert'].includes(key)) return '严重'
  if (['error', 'err'].includes(key)) return '错误'
  if (['warning', 'warn'].includes(key)) return '警告'
  if (['notice', 'info', 'informational'].includes(key)) return '信息'
  if (['debug', 'trace'].includes(key)) return '调试'
  return code
}

function severityColor(code: string) {
  const meta = severityMeta(code)
  return meta?.color || 'arcoblue'
}

async function fetchSeverityMetaList() {
  try {
    const res: any = await fetchSeverityOptions({ enabled: 'true' })
    const payload = res?.details ?? res?.data ?? []
    severityOptions.value = Array.isArray(payload) ? (payload as SeverityOption[]) : []
  } catch (e) {
    console.error('加载告警级别选项失败:', e)
    severityOptions.value = []
  }
}

function truncate(s: string, n: number) {
  if (!s) return '-'
  return s.length <= n ? s : `${s.slice(0, n)}…`
}

function handleFormModelUpdate(v: Record<string, unknown>) {
  formModel.value = v as {
    source_kind: string
    resource_type: string
    resource_id: string
    dispatch_status: string
  }
}

async function fetchList() {
  loading.value = true
  try {
    const res: any = await fetchLogEntries({
      page: pagination.current,
      page_size: pagination.pageSize,
      source_kind: formModel.value.source_kind || undefined,
      resource_type: formModel.value.resource_type || undefined,
      resource_id: formModel.value.resource_id?.trim() || undefined,
      dispatch_status: formModel.value.dispatch_status || undefined,
    })
    const payload = unwrapLogsPayload(res) ?? {}
    tableData.value = payload.items ?? []
    pagination.total = payload.total ?? 0
    if (typeof res.code === 'number' && res.code !== 0) {
      Message.error(res.message || res.msg || '加载失败')
    }
  } catch (e: any) {
    console.error(e)
    Message.error(e?.message || '加载失败')
    tableData.value = []
    pagination.total = 0
  } finally {
    loading.value = false
  }
}

function handleSearch() {
  pagination.current = 1
  fetchList()
}

function handleReset() {
  formModel.value = { source_kind: '', resource_type: '', resource_id: '', dispatch_status: '' }
  pagination.current = 1
  fetchList()
}

function handlePageChange(c: number) {
  pagination.current = c
  fetchList()
}

function handlePageSizeChange(size: number) {
  pagination.pageSize = size
  pagination.current = 1
  fetchList()
}

function handleRefresh() {
  fetchList()
}

function openDetail(row: LogEvent) {
  detailRow.value = row
  detailVisible.value = true
}

function openOutboxDrawer() {
  outboxVisible.value = true
  fetchOutboxList()
}

function showUnparsedQueue() {
  formModel.value = {
    ...formModel.value,
    dispatch_status: 'pending',
  }
  pagination.current = 1
  outboxStatus.value = 'pending'
  fetchList()
  openOutboxDrawer()
}

function handleOutboxSearch() {
  outboxPagination.current = 1
  fetchOutboxList()
}

async function fetchOutboxList() {
  outboxLoading.value = true
  try {
    const res: any = await fetchAlertOutbox({
      page: outboxPagination.current,
      page_size: outboxPagination.pageSize,
      status: outboxStatus.value || undefined,
    })
    const payload = unwrapLogsPayload(res) ?? {}
    outboxRows.value = (payload.items ?? []) as AlertOutbox[]
    outboxPagination.total = payload.total ?? 0
    if (typeof res.code === 'number' && res.code !== 0) {
      Message.error(res.message || res.msg || '队列加载失败')
    }
  } catch (e: any) {
    Message.error(e?.message || '队列加载失败')
    outboxRows.value = []
    outboxPagination.total = 0
  } finally {
    outboxLoading.value = false
  }
}

function handleOutboxPageChange(page: number) {
  outboxPagination.current = page
  fetchOutboxList()
}

function handleOutboxPageSizeChange(size: number) {
  outboxPagination.pageSize = size
  outboxPagination.current = 1
  fetchOutboxList()
}

function handleOutboxRetry(id: number) {
  Modal.confirm({
    title: '确认重试',
    content: `立即重试队列任务 #${id}？`,
    onOk: async () => {
      try {
        const res: any = await retryAlertOutbox(id)
        if (typeof res.code === 'number' && res.code !== 0) {
          Message.error(res.message || res.msg || '重试失败')
          return
        }
        Message.success('已提交重试')
        fetchOutboxList()
      } catch (e: any) {
        Message.error(e?.message || '重试失败')
      }
    },
  })
}

function handleReplayLog(row: LogEvent) {
  Modal.confirm({
    title: '确认重放',
    content: `重放日志 #${row.id} 并重新投递到原始事件池？`,
    onOk: async () => {
      try {
        const res: any = await replayLogEntry(row.id)
        if (typeof res.code === 'number' && res.code !== 0) {
          Message.error(res.message || res.msg || '重放失败')
          return
        }
        Message.success('已提交重放')
        fetchList()
        if (outboxVisible.value) fetchOutboxList()
      } catch (e: any) {
        Message.error(e?.message || '重放失败')
      }
    },
  })
}

function openPayloadModal(payload: string) {
  let text = payload || ''
  try {
    text = JSON.stringify(JSON.parse(payload || '{}'), null, 2)
  } catch {
    // 保持原样，便于排查非标准 JSON。
  }
  payloadText.value = text
  payloadVisible.value = true
}

async function jumpToLogDetail(logEventID: number) {
  if (!logEventID) return
  try {
    const res: any = await fetchLogEntries({
      page: 1,
      page_size: 1,
      log_event_id: logEventID,
    })
    const payload = unwrapLogsPayload(res) ?? {}
    const row = (payload.items?.[0] || null) as LogEvent | null
    if (!row) {
      Message.warning(`未找到日志 #${logEventID}`)
      return
    }
    outboxVisible.value = false
    detailRow.value = row
    detailVisible.value = true
  } catch (e: any) {
    Message.error(e?.message || '日志定位失败')
  }
}

fetchSeverityMetaList()
fetchList()
</script>

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

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

.pre-block {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 12px;
  line-height: 1.5;
  max-height: 280px;
  overflow: auto;
}

.ellipsis-cell {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
