<script setup lang="ts">
import { computed, ref, watch } from "vue";
import type { ChatMessage, RunnerTrace, TraceEvent } from "@/types";
import { firstNonEmptyString } from "@/utils";
import { useBottomFollowScroll } from "@/composables/useBottomFollowScroll";
import { formatDisplayClock } from "@/config/runtime";
import { traceEventBody, traceEventLabel, traceEventMeta } from "@/components/workbench/message-rendering";
import { traceDisplayRows, traceNoiseEventCount } from "../../../../../tools/src/hwlab-cli/trace-renderer.ts";

const props = defineProps<{ message: ChatMessage }>();

const rawListRef = ref<HTMLOListElement | null>(null);
const rawMode = ref(false);
const { following, keepBottomAfterUpdate, onScroll, scrollToBottom, toggleFollowing } = useBottomFollowScroll(rawListRef, { threshold: 24 });

const trace = computed(() => props.message.runnerTrace ?? null);
const events = computed(() => Array.isArray(trace.value?.events) ? trace.value.events : []);
const eventCount = computed(() => trace.value?.eventCount ?? events.value.length);
const readableRows = computed(() => traceDisplayRows(traceRecord(trace.value), events.value as Record<string, unknown>[], { formatClock: formatDisplayClock }));
const noiseCount = computed(() => traceNoiseEventCount(events.value as Record<string, unknown>[]));
const lastEvent = computed(() => events.value.at(-1) ?? null);
const lastReadableRow = computed(() => readableRows.value.at(-1) ?? null);
const lastEventLabel = computed(() => firstNonEmptyString(lastReadableRow.value?.header, trace.value?.lastEventLabel, lastEvent.value?.label, lastEvent.value?.type, lastEvent.value?.kind) ?? "未观测");
const traceId = computed(() => firstNonEmptyString(props.message.traceId, trace.value?.traceId) ?? "pending");
const summaryItems = computed(() => {
  const agentRun = recordValue(trace.value?.agentRun);
  return [
    { label: "status", value: firstNonEmptyString(trace.value?.status, trace.value?.traceStatus) },
    { label: "session", value: firstNonEmptyString(props.message.sessionId, trace.value?.sessionId) },
    { label: "thread", value: firstNonEmptyString(props.message.threadId, trace.value?.threadId) },
    { label: "run", value: firstNonEmptyString(agentRun?.runId, trace.value?.runId) },
    { label: "command", value: firstNonEmptyString(agentRun?.commandId, trace.value?.commandId) },
    { label: "waiting", value: trace.value?.waitingFor }
  ].filter((item): item is { label: string; value: string } => Boolean(item.value));
});

watch(() => [eventCount.value, rawMode.value, following.value] as const, async () => {
  if (!rawMode.value) return;
  await keepBottomAfterUpdate();
});

function eventKey(event: TraceEvent, index: number): string {
  return `${event.ts ?? "no-ts"}-${event.label ?? event.type ?? event.kind ?? "event"}-${index}`;
}

function traceRecord(value: RunnerTrace | null | undefined): Record<string, unknown> {
  return value && typeof value === "object" ? value as Record<string, unknown> : {};
}

function recordValue(value: unknown): Record<string, unknown> | null {
  return value && typeof value === "object" ? value as Record<string, unknown> : null;
}
</script>

<template>
  <section v-if="trace" class="message-trace-debug-panel" aria-label="Trace 调试信息">
    <div class="trace-meta-panel" aria-label="运行记录摘要">
      <dl class="trace-meta-grid">
        <div><dt>trace</dt><dd>{{ traceId }}</dd></div>
        <div><dt>rows</dt><dd>{{ readableRows.length }} / {{ eventCount }}</dd></div>
        <div v-if="noiseCount > 0"><dt>hidden</dt><dd>{{ noiseCount }}</dd></div>
        <div><dt>last</dt><dd>{{ lastEventLabel }}</dd></div>
      </dl>
      <div v-if="summaryItems.length > 0" class="trace-summary-strip" aria-label="Trace identity summary">
        <span v-for="item in summaryItems" :key="item.label" class="trace-summary-chip"><b>{{ item.label }}</b>{{ item.value }}</span>
      </div>
      <p v-if="trace.eventsCompacted" class="trace-warning">当前 result 返回被压缩，前端会自动通过 trace 入口补齐完整事件；完成前不要把压缩窗口当完整 trace。</p>
      <p v-if="noiseCount > 0" class="trace-warning">已聚合隐藏 {{ noiseCount }} 条 backend/chunk/token 噪声事件；原始 payload 仍可在原始事件中审计。</p>
      <div class="trace-meta-actions">
        <button class="btn btn-secondary btn-sm" type="button" @click="toggleFollowing">{{ following ? "跟随" : "暂停" }}</button>
        <button class="btn btn-secondary btn-sm" type="button" @click="scrollToBottom">到底部</button>
        <button class="btn btn-secondary btn-sm" type="button" @click="rawMode = !rawMode">{{ rawMode ? "隐藏原始事件" : "原始事件" }}</button>
      </div>
      <ol v-if="rawMode" ref="rawListRef" class="trace-raw-list" :data-following="following ? 'true' : 'false'" @scroll="onScroll">
        <li v-for="(event, index) in events" :key="eventKey(event, index)" class="trace-raw-row" :data-event-status="event.status || 'pending'">
          <code>{{ traceEventLabel(event) }}</code>
          <span>{{ traceEventBody(event) }}</span>
          <small v-if="traceEventMeta(event)">{{ traceEventMeta(event) }}</small>
        </li>
      </ol>
    </div>
  </section>
</template>
