<template>
  <PermissionGuard widget-id="OpsActionPanel">
    <div class="ops-log-page">
      <header class="ops-stats">
        <div class="ops-stat">
          <span class="ops-stat-label">今日</span>
          <span class="ops-stat-value"><b>{{ counts.today }}</b></span>
        </div>
        <div class="ops-stat-sep" aria-hidden="true" />
        <div class="ops-stat">
          <span class="ops-stat-label">成功</span>
          <span class="ops-stat-value"><b>{{ counts.ok }}</b></span>
        </div>
        <div class="ops-stat">
          <span class="ops-stat-label">失败</span>
          <span class="ops-stat-value" :class="{ 'is-danger': counts.fail > 0 }"><b>{{ counts.fail }}</b></span>
        </div>
        <div class="ops-who">
          {{ displayName }}
          <span>仅本人记录</span>
        </div>
        <div class="ops-stats-actions">
          <el-button :icon="Refresh" :loading="loading" @click="reload">刷新</el-button>
        </div>
      </header>

      <div class="ops-toolbar">
        <el-input
          v-model="search"
          clearable
          placeholder="搜索动作 / 目标 / 说明"
          class="ops-search"
          :prefix-icon="Search"
        />
        <el-select v-model="opFilter" clearable filterable placeholder="动作" class="ops-op">
          <el-option v-for="op in opOptions" :key="op.code" :label="op.label" :value="op.code" />
        </el-select>
        <el-date-picker
          v-model="dateRange"
          type="daterange"
          value-format="YYYY-MM-DD"
          start-placeholder="日起"
          end-placeholder="日止"
          class="ops-date"
          :shortcuts="dateShortcuts"
          unlink-panels
        />
        <span class="ops-count">{{ filteredRows.length }} / {{ rows.length }}</span>
      </div>

      <div class="ops-chips" role="tablist">
        <button
          v-for="chip in statusChips"
          :key="chip.key"
          type="button"
          class="ops-chip"
          :class="{ 'is-active': quickStatus === chip.key }"
          @click="quickStatus = chip.key"
        >
          {{ chip.label }}<b>{{ chip.count }}</b>
        </button>
      </div>

      <div v-loading="loading" class="ops-table-wrap">
        <el-table :data="filteredRows" stripe height="100%" empty-text="暂无你的操作记录">
          <el-table-column label="时间" width="176" sortable :sort-method="(a: OpsAuditEntry, b: OpsAuditEntry) => sortByTime(a.ts, b.ts)">
            <template #default="{ row }">{{ formatTime(row.ts) }}</template>
          </el-table-column>
          <el-table-column label="动作" min-width="140" show-overflow-tooltip>
            <template #default="{ row }">{{ opLabel(row.opCode, row.message) }}</template>
          </el-table-column>
          <el-table-column label="目标" width="120" show-overflow-tooltip>
            <template #default="{ row }">{{ row.target || '—' }}</template>
          </el-table-column>
          <el-table-column label="结果" width="96" align="center">
            <template #default="{ row }">
              <el-tag size="small" :type="isOk(row.result) ? 'success' : 'danger'" effect="plain">
                {{ resultLabel(row.result) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="说明" min-width="200" show-overflow-tooltip>
            <template #default="{ row }">{{ row.message || '—' }}</template>
          </el-table-column>
        </el-table>
      </div>

      <p class="ops-footnote">
        暂停、回库、充电等动作在地图监控里对车辆执行。谁能打开本页，在「权限与角色」里给角色勾选。
      </p>
    </div>
  </PermissionGuard>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { ElMessage } from 'element-plus'
import { Refresh, Search } from '@element-plus/icons-vue'
import PermissionGuard from '@/components/PermissionGuard.vue'
import { listAudits } from '@/api/ops'
import { useAuthStore } from '@/stores/auth'
import { OPS_WHITELIST, type OpsAuditEntry } from '@/types/ops'
import { dateShortcuts, formatTime, isToday, parseTime, sortByTime } from '@/utils/dateTime'

type QuickKey = 'all' | 'ok' | 'fail'

const auth = useAuthStore()
const rows = ref<OpsAuditEntry[]>([])
const loading = ref(false)
const search = ref('')
const opFilter = ref<string | null>(null)
const quickStatus = ref<QuickKey>('all')
const dateRange = ref<[string, string] | null>(null)

const displayName = computed(() => {
  const u = auth.user
  if (!u) return ''
  return u.displayName && u.displayName !== u.username ? `${u.displayName} (${u.username})` : u.username
})

function isOk(result: string) {
  return result === 'ok'
}

function resultLabel(result: string) {
  if (result === 'ok') return '成功'
  if (result === 'unmapped') return '未下发'
  return '失败'
}

function opLabel(code: string, message?: string) {
  if (code === 'ops.car.execute' && message?.trim()) {
    const method = message.replace(/^执行\s+/, '').split('：')[0]?.trim()
    if (method) return method
  }
  return OPS_WHITELIST.find((op) => op.code === code)?.label ?? code
}

function inDateRange(row: OpsAuditEntry) {
  if (!dateRange.value) return true
  const t = parseTime(row.ts)
  if (t == null) return false
  const [from, to] = dateRange.value
  return t >= Date.parse(`${from}T00:00:00`) && t <= Date.parse(`${to}T23:59:59.999`)
}

const opOptions = computed(() => {
  const codes = [...new Set(rows.value.map((r) => r.opCode))]
  return codes.map((code) => ({ code, label: opLabel(code) }))
})

const filteredRows = computed(() => {
  const q = search.value.trim().toLowerCase()
  const tokens = q ? q.split(/\s+/).filter(Boolean) : []
  return rows.value.filter((row) => {
    if (quickStatus.value === 'ok' && !isOk(row.result)) return false
    if (quickStatus.value === 'fail' && isOk(row.result)) return false
    if (opFilter.value && row.opCode !== opFilter.value) return false
    if (!inDateRange(row)) return false
    if (!tokens.length) return true
    const hay = [opLabel(row.opCode, row.message), row.opCode, row.target, row.message, row.result].join(' ').toLowerCase()
    return tokens.every((tok) => hay.includes(tok))
  })
})

const counts = computed(() => ({
  today: rows.value.filter((r) => isToday(r.ts)).length,
  ok: rows.value.filter((r) => isOk(r.result)).length,
  fail: rows.value.filter((r) => !isOk(r.result)).length
}))

const statusChips = computed(() => [
  { key: 'all' as const, label: '全部', count: rows.value.length },
  { key: 'ok' as const, label: '成功', count: counts.value.ok },
  { key: 'fail' as const, label: '失败', count: counts.value.fail }
])

function isMine(row: OpsAuditEntry) {
  const me = (auth.user?.username ?? '').trim().toLowerCase()
  if (!me) return false
  return (row.user ?? '').trim().toLowerCase() === me
}

async function reload() {
  loading.value = true
  try {
    rows.value = (await listAudits()).filter(isMine)
  } catch (err) {
    ElMessage.error(`加载操作记录失败：${(err as Error).message}`)
  } finally {
    loading.value = false
  }
}

onMounted(() => { void reload() })
</script>

<style scoped>
.ops-log-page {
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: calc(100vh - 56px - 36px - 32px);
  min-height: 0;
  color: var(--mg-text-light);
}

.ops-stats {
  flex: none;
  display: flex;
  align-items: center;
  gap: 26px;
  min-height: 54px;
  padding: 8px 18px;
  border-radius: 10px;
  background: rgba(var(--mg-bg-card-rgb), 0.94);
  border: 1px solid var(--mg-veil-border);
  overflow-x: auto;
}
.ops-stat { display: flex; flex-direction: column; gap: 3px; min-width: 56px; }
.ops-stat-label { font-size: 11px; color: var(--mg-text-muted); white-space: nowrap; }
.ops-stat-value {
  font-family: var(--mg-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 17px;
  font-weight: 650;
  line-height: 1.1;
}
.ops-stat-value.is-danger b { color: var(--mg-status-danger); }
.ops-stat-sep { width: 1px; height: 30px; background: var(--mg-veil-border); flex: none; }
.ops-who {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  font-size: 13px;
  color: var(--mg-text-light);
  white-space: nowrap;
}
.ops-who span { font-size: 11px; color: var(--mg-text-muted); }
.ops-stats-actions { display: flex; align-items: center; flex: none; }

.ops-toolbar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-radius: 10px;
  background: rgba(var(--mg-bg-card-rgb), 0.94);
  border: 1px solid var(--mg-veil-border);
}
.ops-search { width: min(260px, 100%); }
.ops-op { width: 160px; }
.ops-date { width: 250px; }
.ops-count { margin-left: auto; font-size: 12px; color: var(--mg-text-muted); font-variant-numeric: tabular-nums; }

.ops-chips { flex: none; display: flex; gap: 8px; flex-wrap: wrap; }
.ops-chip {
  appearance: none;
  border: 1px solid var(--mg-veil-border);
  background: rgba(var(--mg-bg-card-rgb), 0.9);
  color: var(--mg-text-muted);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.ops-chip b { font-family: var(--mg-font-mono); font-variant-numeric: tabular-nums; color: var(--mg-text-light); }
.ops-chip:hover { color: var(--mg-text-light); background: var(--mg-veil-2); }
.ops-chip.is-active {
  color: var(--mg-primary);
  border-color: rgba(var(--mg-primary-rgb), 0.45);
  background: rgba(var(--mg-primary-rgb), 0.12);
}

.ops-table-wrap {
  flex: 1 1 0;
  min-height: 0;
  border-radius: 10px;
  background: rgba(var(--mg-bg-card-rgb), 0.94);
  border: 1px solid var(--mg-veil-border);
  overflow: hidden;
  padding: 4px;
}

.ops-footnote { flex: none; margin: 0; font-size: 12px; color: var(--mg-text-muted); }

@media (max-width: 960px) {
  .ops-search, .ops-date { width: 100%; }
}
</style>
