<template>
  <div class="request-list">
    <!-- 搜索框 -->
    <div class="search">
      <div class="sw">
        <input
          ref="searchInput"
          v-model="logsStore.search"
          class="si"
          placeholder="关键字搜索… (Ctrl+K)"
        />
        <button v-if="logsStore.search" class="si-clear" @click="logsStore.search = ''">✕</button>
      </div>
    </div>
    <!-- 时间筛选 -->
    <div class="tbar">
      <button
        v-for="t in timeTabs"
        :key="t.value"
        class="tb"
        :class="{ a: logsStore.timeFilter === t.value }"
        @click="logsStore.timeFilter = t.value"
      >{{ t.label }}</button>
    </div>
    <!-- 状态筛选 + 计数 -->
    <div class="fbar">
      <button
        v-for="f in statusTabs"
        :key="f.value"
        class="fb"
        :class="{ a: logsStore.statusFilter === f.value }"
        @click="logsStore.statusFilter = f.value"
      >
        {{ f.label }}<span class="fc">{{ counts[f.value] }}</span>
      </button>
    </div>
    <!-- 请求列表 -->
    <div class="rlist" ref="rlistEl">
      <div v-if="!logsStore.filteredReqs.length" class="empty">
        <div class="ic">📭</div><p>暂无请求</p>
      </div>
      <div
        v-for="req in logsStore.filteredReqs"
        :key="req.requestId"
        class="ri"
        :class="[req.status, { sel: req.requestId === logsStore.curRequestId }]"
        @click="selectReq(req.requestId)"
      >
        <span class="st" :class="req.status" />
        <div class="ri-title">
          <span class="seq">#{{ seqNum(req.requestId) }}</span>
          <span class="ri-title-text">{{ req.title || shortModel(req.model) }}</span>
        </div>
        <div class="ri-time">
          <span v-if="req.endTime" class="dur"> 耗时 {{ fmtMs(req.endTime - req.startTime) }}</span>
          <span v-if="req.ttft" class="ttft"> ⚡️{{ fmtMs(req.ttft) }}</span>
          <span class="date">{{ fmtDate(req.startTime) }}</span>
        </div>
        <div class="r1">
          <span class="rid">{{ req.requestId.slice(0, 8) }}</span>
          <span class="rfmt" :class="req.apiFormat">{{ req.apiFormat }}</span>
          <span v-if="req.responseChars" class="rchars">{{ fmtN(req.responseChars) }} chars</span>
        </div>
        <div class="rbd">
          <span v-if="req.stream" class="bg bg-stream">Stream</span>
          <span v-if="req.toolCount > 0" class="bg bg-tool">T:{{ req.toolCount }}</span>
          <span v-if="req.toolCallsDetected > 0" class="bg bg-call">C:{{ req.toolCallsDetected }}</span>
          <span v-if="req.retryCount > 0" class="bg bg-retry">R:{{ req.retryCount }}</span>
          <span v-if="req.continuationCount > 0" class="bg bg-cont">+{{ req.continuationCount }}</span>
          <span v-if="req.thinkingChars > 0" class="bg bg-think">🤔 {{ fmtN(req.thinkingChars) }} chars</span>
          <span v-if="req.status === 'error'" class="bg bg-err">ERR</span>
          <span v-if="req.status === 'intercepted'" class="bg bg-int">INTERCEPT</span>
        </div>
        <div class="rdbar-bg"><div class="rdbar" :style="durStyle(req)" /></div>
        <div v-if="req.error" class="rerr">{{ req.error }}</div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref, nextTick, onMounted, onUnmounted } from 'vue';
import { useLogsStore } from '../stores/logs';

const searchInput = ref<HTMLInputElement | null>(null);
const rlistEl = ref<HTMLElement | null>(null);

function onKeydown(e: KeyboardEvent) {
  if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
    e.preventDefault();
    searchInput.value?.focus();
    return;
  }
  if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
    // 搜索框聚焦时不干预
    if (document.activeElement === searchInput.value) return;
    const list = logsStore.filteredReqs;
    if (!list.length) return;
    e.preventDefault();
    // 主动移除焦点，防止按钮/tab等元素出现 focus 高亮
    (document.activeElement as HTMLElement)?.blur();
    const cur = logsStore.curRequestId;
    const idx = cur ? list.findIndex(r => r.requestId === cur) : -1;
    let next: number;
    if (e.key === 'ArrowUp') next = idx <= 0 ? list.length - 1 : idx - 1;
    else next = idx < 0 || idx >= list.length - 1 ? 0 : idx + 1;
    logsStore.selectRequest(list[next].requestId);
    nextTick(() => {
      const el = rlistEl.value?.querySelectorAll('.ri')[next] as HTMLElement | undefined;
      el?.scrollIntoView({ block: 'nearest' });
    });
  }
}

onMounted(() => { window.addEventListener('keydown', onKeydown); });
onUnmounted(() => { window.removeEventListener('keydown', onKeydown); });

const logsStore = useLogsStore();

const timeTabs = [
  { value: 'all' as const,   label: '全部' },
  { value: 'today' as const, label: '今天' },
  { value: '2d' as const,    label: '两天' },
  { value: '7d' as const,    label: '一周' },
  { value: '30d' as const,   label: '一月' },
];

const statusTabs = [
  { value: 'all' as const,         label: '全部' },
  { value: 'success' as const,     label: '成功' },
  { value: 'error' as const,       label: '错误' },
  { value: 'processing' as const,  label: '处理中' },
  { value: 'intercepted' as const, label: '中断' },
];

const counts = computed(() => {
  const base = logsStore.reqs;
  return {
    all: base.length,
    success: base.filter(r => r.status === 'success').length,
    error: base.filter(r => r.status === 'error').length,
    processing: base.filter(r => r.status === 'processing').length,
    intercepted: base.filter(r => r.status === 'intercepted').length,
  };
});

function fmtDate(ts: number): string {
  const d = new Date(ts);
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  const time = d.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
  return `${month}/${day} ${time}`;
}

function shortModel(model: string): string {
  return model.split('/').pop() ?? model;
}

function fmtN(n: number): string {
  return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n);
}

function durStyle(req: { endTime?: number; startTime: number; status: string }): Record<string, string> {
  if (req.status === 'processing') {
    return { width: '100%', background: 'var(--blue)', animation: 'prog 1.5s ease-in-out infinite' };
  }
  if (!req.endTime) return { width: '0%' };
  const ms = req.endTime - req.startTime;
  // 以 30s 为满格基准
  const pct = Math.min(100, Math.round(ms / 300));
  let color: string;
  if (ms < 3000) color = 'var(--green)';
  else if (ms < 8000) color = 'var(--yellow)';
  else if (ms < 20000) color = '#f97316';
  else color = 'var(--red)';
  return { width: pct + '%', background: color };
}

function fmtMs(ms: number): string {
  return ms >= 1000 ? (ms / 1000).toFixed(2).replace(/\.?0+$/, '') + 's' : ms + 'ms';
}

function seqNum(requestId: string): number {
  const idx = logsStore.reqs.findIndex(r => r.requestId === requestId);
  return idx < 0 ? 0 : logsStore.reqs.length - idx;
}

function selectReq(id: string) {
  if (logsStore.curRequestId === id) {
    logsStore.deselect();
  } else {
    logsStore.selectRequest(id);
  }
}
</script>

<style scoped>
.request-list {
  width: 370px; flex-shrink: 0;
  display: flex; flex-direction: column;
  border-right: 1px solid var(--border);
  background: var(--bg1);
}
[data-theme="dark"] .request-list { background: rgba(22,27,39,.75); }

.search { padding: 8px 10px; border-bottom: 1px solid var(--border); }
.sw { position: relative; }
.sw::before { content: '🔍'; position: absolute; left: 9px; top: 50%; transform: translateY(-50%); font-size: 11px; pointer-events: none; }
.si {
  width: 100%; padding: 6px 28px 6px 28px; font-size: 12px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); color: var(--text);
  font-family: var(--mono); outline: none; transition: border-color .2s;
}
.si:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgba(59,130,246,.12); }
.si::placeholder { color: var(--text-muted); }
.si-clear {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-size: 12px; padding: 0 2px;
  line-height: 1; display: flex; align-items: center;
}
.si-clear:hover { color: var(--text); }

.tbar { padding: 5px 8px; border-bottom: 1px solid var(--border); display: flex; gap: 3px; flex-wrap: wrap; }
.tb {
  padding: 3px 9px; font-size: 10px; font-weight: 500;
  border: 1px solid var(--border); border-radius: 20px;
  background: var(--bg); color: var(--text-muted);
  cursor: pointer; transition: all .15s;
}
.tb:hover { border-color: var(--cyan); color: var(--cyan); }
.tb.a { background: linear-gradient(135deg,#0891b2,#06b6d4); border-color: transparent; color: #fff; }

.fbar { padding: 5px 8px; border-bottom: 1px solid var(--border); display: flex; gap: 3px; flex-wrap: wrap; }
.fb {
  padding: 3px 9px; font-size: 10px; font-weight: 500;
  border: 1px solid var(--border); border-radius: 20px;
  background: var(--bg); color: var(--text-muted);
  cursor: pointer; transition: all .15s;
  display: flex; align-items: center; gap: 3px;
}
.fb:hover { border-color: var(--blue); color: var(--blue); }
.fb.a { background: linear-gradient(135deg,#3b82f6,#6366f1); border-color: transparent; color: #fff; }
.fc { font-size: 9px; font-weight: 700; padding: 0 4px; border-radius: 8px; background: rgba(255,255,255,.2); }
.fb:not(.a) .fc { background: var(--pill-bg); color: var(--text-muted); }

.rlist { overflow-y: auto; flex: 1; padding: 4px 0; }
.empty { padding: 24px; text-align: center; color: var(--text-muted); font-size: 13px; }
.empty .ic { font-size: 20px; margin-bottom: 8px; }

.ri {
  position: relative;
  padding: 9px 12px 6px 14px; cursor: pointer;
  margin: 4px 8px;
  border-radius: 8px;
  border: 1px solid var(--border-faint);
  transition: background .1s, border-color .1s;
  overflow: hidden;
}
.ri:hover { background: var(--hover-bg); border-color: var(--border); }
.ri.sel {
  background: linear-gradient(90deg, color-mix(in srgb, var(--blue) 10%, transparent) 0%, transparent 100%);
  border-color: var(--border-faint);
  border-left: 3px solid var(--blue);
  padding-left: 13px;
}

/* 状态点 — 右上角绝对定位 */
.st {
  position: absolute; top: 10px; right: 10px;
  width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0;
  background: var(--text-muted);
}
.st.success { background: var(--green); }
.st.error { background: var(--red); }
.st.processing { background: var(--yellow); animation: pulse 1s infinite; }
.st.intercepted { background: var(--pink); }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.4} }

/* 标题行 */
.ri-title {
  display: flex; align-items: center; gap: 5px;
  padding-right: 14px; margin-bottom: 3px; min-width: 0;
}
.seq { font-size: 10px; font-family: var(--mono); color: var(--blue); font-weight: 700; flex-shrink: 0; }
.ri-title-text {
  font-size: 12px; font-weight: 600; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1;
}

/* 时间行 */
.ri-time {
  display: flex; align-items: center; gap: 4px;
  font-size: 10px; font-family: var(--mono);
  color: var(--text-muted); margin-bottom: 3px;
}
.ri-time .date { margin-left: auto; }
.ri-time .dur { color: var(--text-muted); }
.ri-time .ttft { color: var(--yellow); }

/* requestId + apiFormat + 字数行 */
.r1 { display: flex; align-items: center; gap: 5px; margin-bottom: 4px; }
.rid { font-size: 10px; font-family: var(--mono); color: var(--text-muted); flex-shrink: 0; }
.rfmt {
  font-size: 9px; font-weight: 700; padding: 1px 5px;
  border-radius: 3px; text-transform: uppercase;
  background: var(--pill-bg); color: var(--text-muted);
}
.rfmt.anthropic { background: #7c3aed22; color: #a78bfa; }
.rfmt.openai { background: #05966922; color: #34d399; }
.rfmt.responses { background: #0ea5e9 22; color: #38bdf8; }
.rchars { font-size: 10px; font-family: var(--mono); color: var(--text-muted); margin-left: auto; }

/* badges 行 */
.rbd { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 5px; }
.bg {
  font-size: 9px; font-weight: 600; padding: 1px 5px;
  border-radius: 3px; line-height: 1.5;
}
.bg-stream { background: color-mix(in srgb, var(--green) 15%, transparent); color: var(--green); }
.bg-tool { background: color-mix(in srgb, var(--blue) 15%, transparent); color: var(--blue); }
.bg-call { background: color-mix(in srgb, var(--cyan) 15%, transparent); color: var(--cyan); }
.bg-retry { background: color-mix(in srgb, var(--yellow) 15%, transparent); color: var(--yellow); }
.bg-cont { background: color-mix(in srgb, var(--purple) 15%, transparent); color: var(--purple); }
.bg-think { background: color-mix(in srgb, var(--text-muted) 15%, transparent); color: var(--text-muted); }
.bg-err { background: color-mix(in srgb, var(--red) 15%, transparent); color: var(--red); }
.bg-int { background: color-mix(in srgb, var(--pink) 15%, transparent); color: var(--pink); }

/* 进度条 — 百分比效果 */
.rdbar-bg {
  height: 3px;
  background: var(--border-faint);
  margin: 4px 0 0 0;
  border-radius: 2px;
  overflow: hidden;
}
.rdbar {
  height: 100%;
  border-radius: 2px;
  transition: width .4s ease;
}
@keyframes prog { 0%,100%{opacity:.4} 50%{opacity:1} }

.rerr { color: var(--red); margin-top: 3px; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
</style>
