<script setup lang="ts">
// SPEC: PJ2026-010401080313 Workbench实时权威 draft-2026-07-09-p1-single-step-debug; PJ2026-0106050514 Workbench实时运行面 draft-2026-06-30-p0-1297-spec-first.
// Responsibility: shared Workbench message card and TraceTimeline rendering used by production Workbench and debug harnesses.

import { computed } from "vue";
import type { ChatMessage } from "@/types";
import ApiErrorDiagnostic from "@/components/common/ApiErrorDiagnostic.vue";
import LoadingState from "@/components/common/LoadingState.vue";
import StatusBadge from "@/components/common/StatusBadge.vue";
import TraceTimeline from "@/components/agent/TraceTimeline.vue";
import { useWorkbenchNowTicker } from "@/composables/useWorkbenchNowTicker";
import { workbenchTraceTimelinePolicy } from "@/config/runtime";
import { messageDiagnosticView } from "@/utils/workbench-error-runtime";
import MessageMarkdown from "./MessageMarkdown.vue";
import { traceLifecycleExpanded } from "./trace-lifecycle";

const props = withDefaults(defineProps<{
  message: ChatMessage;
  detailEnabled?: boolean;
  traceAutoExpanded?: boolean | null;
  storageKeyPrefix?: string;
}>(), {
  detailEnabled: true,
  traceAutoExpanded: null,
  storageKeyPrefix: "hwlab.workbench.trace-open"
});

const emit = defineEmits<{
  details: [message: ChatMessage];
}>();

const running = computed(() => isRunningMessage(props.message));
const { nowMs } = useWorkbenchNowTicker(running);
const traceTimelinePolicy = computed(() => workbenchTraceTimelinePolicy());
const diagnostic = computed(() => messageDiagnosticView(props.message));
const visibleText = computed(() => visibleMessageText(props.message));
const durationMeta = computed(() => messageDurationMeta(props.message));
const activityMeta = computed(() => messageActivityMeta(props.message));
const autoExpanded = computed(() => props.traceAutoExpanded ?? traceLifecycleExpanded(props.message, traceTimelinePolicy.value));

function isRunningMessage(message: ChatMessage): boolean {
  return message.role === "agent" && ["pending", "running"].includes(String(message.status ?? "").toLowerCase());
}

function isTerminalAgentMessage(message: ChatMessage): boolean {
  if (message.role !== "agent") return false;
  return ["completed", "failed", "blocked", "timeout", "canceled", "cancelled", "stale", "thread-resume-failed"].includes(String(message.status ?? "").trim().toLowerCase().replace(/_/gu, "-"));
}

function visibleMessageText(message: ChatMessage): string {
  const text = String(message.text ?? "").trim();
  if (message.role === "agent" && !isTerminalAgentMessage(message)) return "";
  return text;
}

function showStatusBadge(message: ChatMessage): boolean {
  return message.role !== "agent" || !isRunningMessage(message);
}

function isAwaitingAgentBody(message: ChatMessage): boolean {
  return isRunningMessage(message) && !message.runnerTrace && !visibleMessageText(message);
}

function messageDurationMeta(message: ChatMessage): { text: string; label: string } | null {
  if (message.role !== "agent") return null;
  const timing = messageTimingForDisplay(message);
  const durationMs = isRunningMessage(message) ? durationSince(timing?.startedAt) : terminalMessageDurationMs(timing);
  if (durationMs === null) return null;
  const value = formatDuration(durationMs);
  return { text: `耗时 ${value}`, label: `总耗时：${value}` };
}

function messageActivityMeta(message: ChatMessage): { text: string; label: string } | null {
  if (!isRunningMessage(message)) return null;
  const timing = messageTimingForDisplay(message);
  const ageMs = durationSince(timing?.lastEventAt);
  if (ageMs === null) return null;
  const value = formatDuration(ageMs) + "前";
  return { text: `最近更新：${value}`, label: `最近更新：${value}` };
}

function messageTimingForDisplay(message: ChatMessage): ChatMessage["timing"] {
  return message.timing ?? null;
}

function terminalMessageDurationMs(timing: ChatMessage["timing"]): number | null {
  const durationMs = finiteDurationMs(timing?.durationMs);
  if (durationMs === 0 && timing?.finishedAt) return 1000;
  return durationMs;
}

function durationSince(timestamp: unknown): number | null {
  const startedAtMs = timestampMs(timestamp);
  return startedAtMs === null ? null : Math.max(0, nowMs.value - startedAtMs);
}

function timestampMs(value: unknown): number | null {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  if (!trimmed) return null;
  const parsed = Date.parse(trimmed);
  return Number.isFinite(parsed) ? parsed : null;
}

function finiteDurationMs(value: unknown): number | null {
  const number = Number(value);
  return Number.isFinite(number) && number >= 0 ? Math.trunc(number) : null;
}

function formatDuration(ms: number): string {
  const seconds = ms > 0 && ms < 1000 ? 1 : Math.max(0, Math.floor(ms / 1000));
  if (seconds < 60) return `${seconds} 秒`;
  const minutes = Math.floor(seconds / 60);
  if (minutes < 60) return `${minutes} 分 ${seconds % 60} 秒`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours} 小时 ${minutes % 60} 分`;
  const days = Math.floor(hours / 24);
  return `${days} 天 ${hours % 24} 小时`;
}

function traceStorageKey(message: ChatMessage): string {
  return `${props.storageKeyPrefix}.${message.traceId ?? message.runnerTrace?.traceId ?? message.id}`;
}

</script>

<template>
  <article
    class="message-card"
    :data-role="message.role"
    :data-status="message.status"
    :data-session-id="message.sessionId || undefined"
    :data-message-id="message.id || undefined"
    :data-trace-id="message.traceId || message.runnerTrace?.traceId || undefined"
    :data-turn-id="message.turnId || message.traceId || message.runnerTrace?.traceId || undefined"
  >
    <TraceTimeline v-if="message.role === 'agent' && message.runnerTrace" :trace="message.runnerTrace" :auto-expanded="autoExpanded" :storage-key="traceStorageKey(message)" final-response-placement="outer" :outer-final-response-text="visibleText" />
    <LoadingState v-if="isAwaitingAgentBody(message)" class="message-loading" label="启动中..." compact />
    <MessageMarkdown v-if="visibleText" class="message-text" :source="visibleText" />
    <ApiErrorDiagnostic v-if="diagnostic.visible" class="message-diagnostic projection-diagnostic" :error="diagnostic.text" :api-error="diagnostic.apiError" :diagnostic="diagnostic.diagnostic" :show-message="diagnostic.showMessage" compact />
    <footer v-if="message.role !== 'user'" class="message-meta-footer">
      <div v-if="running || activityMeta" class="message-activity-group">
        <span v-if="running" class="agent-running-indicator" aria-label="运行中" role="status"><span aria-hidden="true" /></span>
        <span v-if="activityMeta" class="message-activity-meta" :aria-label="activityMeta.label" :title="activityMeta.label">{{ activityMeta.text }}</span>
      </div>
      <div class="message-header-actions">
        <StatusBadge v-if="showStatusBadge(message)" :status="message.status" />
        <span v-if="durationMeta" class="message-timing-meta message-duration-meta" :aria-label="durationMeta.label" :title="durationMeta.label">{{ durationMeta.text }}</span>
        <button v-if="detailEnabled && message.role === 'agent'" class="message-detail-button" type="button" aria-label="运行详情" title="运行详情" @click="emit('details', message)">!</button>
      </div>
    </footer>
  </article>
</template>
