<script setup lang="ts">
import { NDataTable, NInput, NModal, type DataTableColumns } from 'naive-ui'
import { computed, h, reactive, ref } from 'vue'

import { listSystemLogs } from '@/api/settings'
import DetailFieldList, { type DetailRow } from '@/components/DetailFieldList.vue'
import DetailHero, { type HeroChip } from '@/components/DetailHero.vue'
import JsonBlock from '@/components/JsonBlock.vue'
import PanelHeader from '@/components/PanelHeader.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import { actionLabel } from '@/composables/useActionLabel'
import { useAutoRefresh } from '@/composables/useAutoRefresh'
import { fmtTime } from '@/composables/useFormat'
import { useIsMobile } from '@/composables/useIsMobile'
import { useMobileModal } from '@/composables/useMobileModal'
import type { SystemLog } from '@/types/api'
import { useToast } from '@/composables/useToast'

const isMobile = useIsMobile()
const { modalStyle, modalClass } = useMobileModal(640)
const message = useToast()

const rows = ref<SystemLog[]>([])
const loading = ref(false)
const keyword = ref('')
// 已生效的关键字：回车 / 清除时更新，翻页沿用
const applied = ref('')

/** 受控远程分页：字面量对象会在重渲染时被重建导致状态丢失 */
const pagination = reactive({
  page: 1,
  pageSize: 20,
  itemCount: 0,
  showSizePicker: true,
  pageSizes: [20, 50, 100],
  // 移动端页码槽位放不下会被裁切,切 simple 形态(‹ n/总页 ›)
  get simple() {
    return isMobile.value
  },
  onUpdatePage: (p: number) => {
    pagination.page = p
    void load()
  },
  onUpdatePageSize: (ps: number) => {
    pagination.pageSize = ps
    pagination.page = 1
    void load()
  },
})

// 列表请求代次:翻页与自动刷新共用 load,旧响应(如刷新的第 1 页)晚到时
// 不得覆盖已切换的新页
let loadSeq = 0

async function load(silent = false) {
  const seq = ++loadSeq
  if (!silent) loading.value = true
  try {
    const data = await listSystemLogs({
      page: pagination.page,
      pageSize: pagination.pageSize,
      keyword: applied.value || undefined,
    })
    if (seq !== loadSeq) return
    rows.value = data.items
    pagination.itemCount = data.total
  } catch (e) {
    if (!silent) message.error(e instanceof Error ? e.message : '加载系统日志失败')
  } finally {
    if (!silent && seq === loadSeq) loading.value = false
  }
}

function search() {
  applied.value = keyword.value.trim()
  pagination.page = 1
  void load()
}

function onClear() {
  keyword.value = ''
  search()
}

// 行点击详情:与回传/审计日志一致的「字段 + 原始 JSON」风格
const detail = ref<SystemLog | null>(null)
const showDetail = ref(false)

function rowProps(row: SystemLog) {
  return {
    style: 'cursor: pointer',
    onClick: () => {
      detail.value = row
      showDetail.value = true
    },
  }
}

/** 摘要头 chips:用户 / IP / 时间 */
const heroChips = computed<HeroChip[]>(() => {
  const d = detail.value
  if (!d) return []
  return [
    { label: '用户', value: d.username || '—' },
    { label: 'IP', value: d.clientIp, mono: true },
    { label: '时间', value: fmtTime(d.createdAt) },
  ]
})

/** 字段网格:头部未覆盖的补充字段;失败原因红字置顶 */
const detailRows = computed<DetailRow[]>(() => {
  const d = detail.value
  if (!d) return []
  return [
    ...(d.errMsg ? [{ label: '失败原因', value: d.errMsg, tone: 'err' as const, span: 2 as const }] : []),
    { label: 'User-Agent', value: d.userAgent, mono: true, span: 2 as const, ellipsis: true },
  ]
})

const columns: DataTableColumns<SystemLog> = [
  {
    title: '时间',
    key: 'createdAt',
    width: 150,
    render: (row) =>
      h('span', { class: 'text-[13px] whitespace-nowrap text-ink-2' }, fmtTime(row.createdAt)),
  },
  {
    title: '用户',
    key: 'username',
    width: 110,
    ellipsis: { tooltip: true },
    render: (row) => h('span', { class: 'text-[13px]' }, row.username || '—'),
  },
  {
    // 主行中文动作、副行原始接口,可读性与可检索性兼顾
    title: '动作',
    key: 'path',
    minWidth: 280,
    render: (row) =>
      h('div', { class: 'min-w-0' }, [
        h('div', { class: 'truncate text-[13px]' }, actionLabel(row.method, row.path) || '—'),
        h('div', { class: 'mono mt-px truncate text-xs text-ink-3' }, `${row.method} ${row.path}`),
      ]),
  },
  {
    title: '状态',
    key: 'status',
    width: 70,
    render: (row) =>
      h(
        'span',
        { class: ['text-[13px] tabular-nums', row.status >= 400 ? 'text-err' : ''] },
        String(row.status),
      ),
  },
  {
    title: '耗时',
    key: 'durationMs',
    width: 90,
    render: (row) =>
      h('span', { class: 'text-[13px] tabular-nums text-ink-2' }, `${row.durationMs} ms`),
  },
  {
    title: 'IP',
    key: 'clientIp',
    width: 140,
    render: (row) => h('span', { class: 'mono text-[12.5px] text-ink-2' }, row.clientIp),
  },
]

void load()
useAutoRefresh(() => load(true))
</script>

<template>
  <div class="panel">
    <PanelHeader title="系统日志" desc="面板写操作与登录成败留痕，用于审计与排障">
      <NInput
        v-if="!isMobile"
        v-model:value="keyword"
        size="small"
        clearable
        placeholder="搜索路径 / 用户名"
        class="!w-64"
        @keyup.enter="search"
        @clear="onClear"
      />
    </PanelHeader>
    <!-- 移动端筛选独立成行全宽,不与标题挤同一行 -->
    <div v-if="isMobile" class="border-b border-line-soft px-4.5 py-2.5">
      <NInput
        v-model:value="keyword"
        size="small"
        clearable
        placeholder="搜索路径 / 用户名"
        @keyup.enter="search"
        @clear="onClear"
      />
    </div>
    <NDataTable
      remote
      :columns="columns"
      :data="rows"
      :loading="loading"
      :pagination="pagination"
      :scroll-x="880"
      :row-key="(r: SystemLog) => r.id"
      :row-props="rowProps"
    />
    <NModal v-model:show="showDetail" preset="card" closable :style="modalStyle" :class="modalClass">
      <template #header>
        <DetailHero
          v-if="detail"
          kind="系统日志 · 面板操作"
          :title="actionLabel(detail.method, detail.path) || `${detail.method} ${detail.path}`"
          :sub="`${detail.method} ${detail.path} · ${detail.durationMs} ms`"
          :chips="heroChips"
        >
          <StatusBadge
            :kind="detail.status >= 400 ? 'term' : 'run'"
            :label="`${detail.status >= 400 ? '失败' : '成功'} ${detail.status}`"
            :dot="false"
          />
        </DetailHero>
      </template>
      <div v-if="detail" class="flex flex-col gap-3.5">
        <DetailFieldList :rows="detailRows" :cols="2" />
        <JsonBlock :value="detail" title="原始记录" meta="仅元数据,不含请求体" max-height="32vh" wide />
      </div>
    </NModal>
  </div>
</template>
