<template>
  <div class="audit-log">
    <header class="page-header">
      <h1>{{ t('auditLog.title') }}</h1>
      <div class="header-actions">
        <button class="btn btn-ghost" @click="reload" :disabled="loading">
          <svg v-if="loading" class="audit-spinner" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 12a9 9 0 11-6.219-8.56"/></svg>
          {{ t('auditLog.refresh') }}
        </button>
      </div>
    </header>

    <p class="page-desc">{{ t('auditLog.desc') }}</p>

    <!-- 筛选栏 -->
    <div class="filter-bar audit-filters">
      <select v-model="statusFilter" class="audit-filter-select">
        <option value="">{{ t('auditLog.filter.allStatus') }}</option>
        <option v-for="s in statusKeys" :key="s" :value="s">{{ t(`auditLog.status.${s}`) }}</option>
      </select>
      <select v-model="riskFilter" class="audit-filter-select">
        <option value="">{{ t('auditLog.filter.allRisk') }}</option>
        <option v-for="r in riskKeys" :key="r" :value="r">{{ t(`auditLog.risk.${r}`) }}</option>
      </select>
      <div class="search-box audit-search">
        <input v-model="toolSearch" type="text" :placeholder="t('auditLog.filter.toolPlaceholder')" />
      </div>
    </div>

    <!-- 状态条 -->
    <div v-if="loading" class="empty-state audit-loading">
      <svg class="audit-spinner audit-spinner--lg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 12a9 9 0 11-6.219-8.56"/></svg>
    </div>
    <div v-else-if="errorMsg" class="empty-state audit-error">⚠ {{ errorMsg }}</div>
    <div v-else-if="filteredRecords.length === 0" class="empty-state">{{ t('auditLog.empty') }}</div>

    <!-- 表格 -->
    <div v-else class="audit-table-wrap">
      <table class="audit-table">
        <thead>
          <tr>
            <th class="col-time">{{ t('auditLog.col.time') }}</th>
            <th class="col-tool">{{ t('auditLog.col.tool') }}</th>
            <th class="col-risk">{{ t('auditLog.col.risk') }}</th>
            <th class="col-status">{{ t('auditLog.col.status') }}</th>
            <th class="col-decided">{{ t('auditLog.col.decided') }}</th>
            <th class="col-args">{{ t('auditLog.col.args') }}</th>
            <th class="col-result">{{ t('auditLog.col.result') }}</th>
          </tr>
        </thead>
        <tbody>
          <template v-for="r in filteredRecords" :key="r.id">
            <tr class="audit-row" :class="{ expanded: expandedId === r.id }" @click="toggleExpand(r.id)">
              <td class="col-time">
                <div class="time-rel">{{ formatRelative(r.requested_at) }}</div>
                <div class="time-abs">{{ formatDate(r.requested_at) }}</div>
                <div v-if="r.executed_at" class="time-dur">⏱ {{ durationLabel(r.requested_at, r.executed_at) }}</div>
              </td>
              <td class="col-tool"><code class="tool-name">{{ r.tool_name }}</code></td>
              <td class="col-risk">
                <span class="risk-badge" :class="riskClass(r.risk_level)">{{ riskLabel(r.risk_level) }}</span>
              </td>
              <td class="col-status">
                <span class="status-tag" :class="statusClass(r.status)">{{ statusLabel(r.status) }}</span>
              </td>
              <td class="col-decided">
                <span v-if="r.decided_by" class="decided-tag" :class="decidedClass(r.decided_by)">{{ decidedLabel(r.decided_by) }}</span>
                <span v-else class="decided-none">—</span>
              </td>
              <td class="col-args"><code class="brief">{{ r.arguments_brief || '—' }}</code></td>
              <td class="col-result"><code class="brief">{{ r.result_brief || '—' }}</code></td>
            </tr>
            <tr v-if="expandedId === r.id" class="audit-detail-row">
              <td colspan="7">
                <div class="audit-detail">
                  <div class="detail-section">
                    <span class="detail-label">{{ t('auditLog.col.args') }}</span>
                    <pre class="detail-pre">{{ r.arguments_brief || '—' }}</pre>
                  </div>
                  <div class="detail-section">
                    <span class="detail-label">{{ t('auditLog.col.result') }}</span>
                    <pre class="detail-pre">{{ r.result_brief || '—' }}</pre>
                  </div>
                </div>
              </td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>

    <!-- 分页 -->
    <div v-if="!loading && !errorMsg" class="pager">
      <button class="btn btn-ghost btn-sm" :disabled="offset === 0" @click="prevPage">{{ t('auditLog.pager.prev') }}</button>
      <span class="pager-info">{{ t('auditLog.pager.page', { n: page }) }}{{ hasMore ? '' : t('auditLog.pager.last') }}<span class="pager-total">{{ t('auditLog.pager.total', { n: total }) }}</span></span>
      <button class="btn btn-ghost btn-sm" :disabled="!hasMore" @click="nextPage">{{ t('auditLog.pager.next') }}</button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { invoke } from '@tauri-apps/api/core'
import { formatRelative, formatDate } from '@/utils/time'
import { usePersistedRef } from '@/composables/usePersistedRef'

const { t } = useI18n()

// 本地 interface(不进 api/types.ts,避撞 B-42 agent)
// 字段与后端 ToolExecutionDto(audit.rs) 一一对应
interface ToolExecutionRecord {
  id: string
  conversation_id: string | null
  tool_call_id: string
  tool_name: string
  arguments_brief: string
  result_brief: string | null
  status: string
  risk_level: string
  requested_at: string
  executed_at: string | null
  decided_by: string | null
}

// 后端分页结果 {items,total,has_more}(对标项目通用结构)
interface ToolExecutionPage {
  items: ToolExecutionRecord[]
  total: number
  has_more: boolean
}

const PAGE_SIZE = 50
const records = ref<ToolExecutionRecord[]>([])
const total = ref(0)
// hasMore 来自后端(基于 offset+items.len()<total 推断),非"本页满 PAGE_SIZE"启发式
const hasMore = ref(false)
const offset = ref(0)
const loading = ref(false)
const errorMsg = ref('')

// 筛选状态(后端 WHERE 收口,非客户端 filter 当前页)。持久化到 localStorage。
// 空串 = 不过滤(对应后端 Option<String>=None)。
const statusFilter = usePersistedRef('auditLog.statusFilter', '')
const riskFilter = usePersistedRef('auditLog.riskFilter', '')
const toolSearch = usePersistedRef('auditLog.toolSearch', '')

// 展开行(查看完整 args/result)
const expandedId = ref<string | null>(null)

// interrupted 来自 cleanup_stale_pending(超时 pending 标记),与 statusKeys 一并展示
const statusKeys = ['pending', 'approved', 'rejected', 'executing', 'completed', 'failed', 'interrupted']
const riskKeys = ['low', 'medium', 'high']

const page = computed(() => Math.floor(offset.value / PAGE_SIZE) + 1)

// 筛选条件变化 → 重置分页 + 关闭展开行 + 重拉(后端 WHERE)
// 下拉框(status/risk)即变即查;工具名自由文本输入走 300ms 防抖(对齐 Tasks.vue searchKeyword)。
watch([statusFilter, riskFilter], () => {
  offset.value = 0
  expandedId.value = null
  void load()
})

let _searchTimer: ReturnType<typeof setTimeout> | null = null
watch(toolSearch, () => {
  if (_searchTimer) clearTimeout(_searchTimer)
  _searchTimer = setTimeout(() => {
    offset.value = 0
    expandedId.value = null
    void load()
  }, 300)
})

onUnmounted(() => {
  if (_searchTimer) clearTimeout(_searchTimer)
})

// 列表展示用 records(后端已按筛选条件过滤,前端不再二次 filter)
const filteredRecords = computed(() => records.value)

function toggleExpand(id: string) {
  expandedId.value = expandedId.value === id ? null : id
}

// 审批→执行 耗时(秒级,毫秒差太精细)
function durationLabel(requested: string, executed: string): string {
  const ms = new Date(executed).getTime() - new Date(requested).getTime()
  if (isNaN(ms) || ms < 0) return '—'
  if (ms < 1000) return '<1s'
  const s = Math.floor(ms / 1000)
  if (s < 60) return `${s}s`
  const m = Math.floor(s / 60)
  return `${m}m${s % 60}s`
}

// 构造后端查询参数(空串/纯空白 → undefined = 不过滤)
function buildQuery() {
  const q: {
    status?: string
    risk_level?: string
    tool_keyword?: string
    limit: number
    offset: number
  } = { limit: PAGE_SIZE, offset: offset.value }
  if (statusFilter.value) q.status = statusFilter.value
  if (riskFilter.value) q.risk_level = riskFilter.value
  const kw = toolSearch.value.trim()
  if (kw) q.tool_keyword = kw
  return q
}

async function load() {
  loading.value = true
  errorMsg.value = ''
  try {
    const result = await invoke<ToolExecutionPage>('list_tool_executions', {
      query: buildQuery(),
    })
    records.value = result.items
    total.value = result.total
    hasMore.value = result.has_more
    // 后端 total 已正确,空结果即真无数据(不再像旧"满页推断"需回退)
  } catch (e) {
    errorMsg.value = String(e)
  } finally {
    loading.value = false
  }
}

function reload() {
  if (loading.value) return
  offset.value = 0
  expandedId.value = null
  void load()
}
function prevPage() {
  if (offset.value === 0) return
  offset.value = Math.max(0, offset.value - PAGE_SIZE)
  expandedId.value = null
  void load()
}
function nextPage() {
  if (!hasMore.value) return
  offset.value += PAGE_SIZE
  expandedId.value = null
  void load()
}

// ── 标签 / 样式映射 ──
// 文本走 i18n(auditLog.risk/status/decided);class 映射不国际化
function riskLabel(r: string): string {
  return t(`auditLog.risk.${r}`)
}
function riskClass(r: string): string {
  return ({ low: 'risk-low', medium: 'risk-medium', high: 'risk-high' } as Record<string, string>)[r] ?? 'risk-low'
}
function statusLabel(s: string): string {
  return t(`auditLog.status.${s}`)
}
function statusClass(s: string): string {
  return ({
    pending: 'status-pending',
    approved: 'status-approved',
    rejected: 'status-rejected',
    executing: 'status-executing',
    completed: 'status-completed',
    failed: 'status-failed',
    interrupted: 'status-interrupted',
  } as Record<string, string>)[s] ?? 'status-pending'
}
function decidedLabel(d: string): string {
  return t(`auditLog.decided.${d}`)
}
function decidedClass(d: string): string {
  return d === 'human' ? 'decided-human' : 'decided-auto'
}

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

<style scoped>
.audit-log {
  padding: var(--df-pad-page, 24px);
  max-width: 1200px;
}

/* page-header / btn 等已提取到 global.css 全局 */

.page-desc {
  font-size: 12px;
  color: var(--df-text-dim);
  margin-bottom: 16px;
}

/* ===== 筛选栏 ===== */
.audit-filters { gap: 8px; margin-bottom: 16px; }
.audit-filter-select {
  padding: 6px 10px;
  border: 0.5px solid var(--df-border);
  border-radius: var(--df-radius-sm);
  background: var(--df-bg);
  color: var(--df-text);
  font-size: 12px;
  cursor: pointer;
}
.audit-filter-select:focus { outline: none; border-color: var(--df-accent); }
.audit-search { max-width: 200px; }

/* ===== 加载/错误状态 ===== */
.audit-loading { display: flex; align-items: center; justify-content: center; padding: 40px; }
.audit-error { color: var(--df-danger); }
.audit-spinner { animation: audit-spin 0.9s linear infinite; }
.audit-spinner--lg { color: var(--df-text-dim); }
@keyframes audit-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* ===== 表格 ===== */
.audit-table-wrap {
  background: var(--df-bg-card);
  border: 0.5px solid var(--df-border);
  border-radius: var(--df-radius-lg);
  overflow: auto;
}
.audit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.audit-table thead th {
  text-align: left;
  padding: 10px 12px;
  font-weight: 500;
  font-size: 11px;
  color: var(--df-text-dim);
  border-bottom: 0.5px solid var(--df-border);
  white-space: nowrap;
  position: sticky;
  top: 0;
  background: var(--df-bg-card);
}
.audit-table tbody td {
  padding: 8px 12px;
  border-bottom: 0.5px solid var(--df-border);
  color: var(--df-text-secondary);
  vertical-align: top;
}
.audit-table tbody tr:last-child td { border-bottom: none; }
.audit-table tbody tr:hover td { background: var(--df-bg-hover, rgba(255,255,255,0.03)); }
/* 展开/hover 行高亮由 .audit-row:hover/.expanded 管理,避免选择器冲突 */
.audit-table tbody tr.audit-row:hover td { background: var(--df-bg-hover, rgba(255,255,255,0.03)); }

.col-time { min-width: 120px; }
.col-tool { min-width: 130px; }
.col-risk { width: 56px; }
.col-status { width: 80px; }
.col-decided { width: 64px; }
.col-args { min-width: 200px; max-width: 320px; }
.col-result { min-width: 220px; max-width: 360px; }
/* 表格内 args/result 单元格限高 + 截断(长内容不撑爆行),完整内容点行展开查 detail-pre */
.col-args .brief, .col-result .brief {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  max-height: 48px; /* 约 3 行(行高 ~16px),超长内容折叠,展开行看完整 */
}

/* 可点击行 */
.audit-row { cursor: pointer; transition: background 0.12s; }
.audit-row:hover td { background: var(--df-bg-hover, rgba(255,255,255,0.03)); }
.audit-row.expanded td { background: var(--df-bg-hover, rgba(255,255,255,0.04)); }

/* 展开详情行 */
.audit-detail-row td { padding: 0 !important; border-bottom: 0.5px solid var(--df-border); }
.audit-detail { padding: 12px 16px; display: flex; flex-direction: column; gap: 10px; }
.detail-section { display: flex; flex-direction: column; gap: 4px; }
.detail-label { font-size: 11px; color: var(--df-text-dim); text-transform: uppercase; letter-spacing: 0.5px; }
.detail-pre {
  margin: 0; padding: 8px 10px;
  background: var(--df-bg);
  border: 0.5px solid var(--df-border);
  border-radius: var(--df-radius-sm);
  font-family: var(--df-font-mono);
  font-size: 11px;
  color: var(--df-text-secondary);
  white-space: pre-wrap;
  word-break: break-all;
  max-height: 200px;
  overflow-y: auto;
}

.time-rel { font-size: 12px; color: var(--df-text-secondary); }
.time-abs { font-size: 10px; color: var(--df-text-dim); margin-top: 2px; font-family: var(--df-font-mono); }
.time-dur { font-size: 10px; color: var(--df-text-dim); margin-top: 2px; font-family: var(--df-font-mono); }

.tool-name {
  font-family: var(--df-font-mono);
  font-size: 11px;
  color: var(--df-accent);
  background: var(--df-accent-bg);
  padding: 1px 6px;
  border-radius: var(--df-radius-xs);
}

.brief {
  font-family: var(--df-font-mono);
  font-size: 11px;
  color: var(--df-text-secondary);
  word-break: break-all;
  white-space: pre-wrap;
}

/* 风险 badge */
.risk-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--df-radius-xs);
}
.risk-low { background: rgba(90,99,128,0.2); color: var(--df-text-dim); }
.risk-medium { background: var(--df-warning-bg); color: var(--df-warning); }
.risk-high { background: var(--df-danger-bg); color: var(--df-danger); }

/* 状态 tag */
.status-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 3px 10px;
  border-radius: var(--df-radius-sm);
  white-space: nowrap;
}
.status-pending { background: var(--df-warning-bg); color: var(--df-warning); }
.status-approved { background: var(--df-success-bg); color: var(--df-success); }
.status-rejected { background: var(--df-danger-bg); color: var(--df-danger); }
.status-executing { background: var(--df-info-bg); color: var(--df-info); }
.status-completed { background: var(--df-success-bg); color: var(--df-success); }
.status-failed { background: var(--df-danger-bg); color: var(--df-danger); }
.status-interrupted { background: rgba(90,99,128,0.2); color: var(--df-text-dim); }

/* 决策者 tag */
.decided-tag {
  display: inline-block;
  font-size: 10px;
  font-weight: 500;
  padding: 2px 8px;
  border-radius: var(--df-radius-xs);
}
.decided-human { background: var(--df-accent-bg); color: var(--df-accent); }
.decided-auto { background: rgba(90,99,128,0.2); color: var(--df-text-dim); }
.decided-none { color: var(--df-text-dim); }

/* ===== 分页 ===== */
.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 20px;
}
.pager-info {
  font-size: 12px;
  color: var(--df-text-dim);
  font-family: var(--df-font-mono);
}
.pager-total {
  margin-left: 8px;
  color: var(--df-text-dim);
  opacity: 0.7;
}

/* empty-state 已提取到 global.css 全局 */
</style>
