<script setup lang="ts">
// SPEC: PJ2026-010401080313 Workbench实时权威 draft-2026-07-09-p1-single-step-debug.
// Responsibility: Workbench debug route for backend-driven fake SSE single-step Trace card inspection.

import { computed, onBeforeUnmount, onMounted, ref } from "vue";
import { connectWorkbenchDebugFakeSse, connectWorkbenchKafkaSseDebug, workbenchDebugAPI, workbenchKafkaSseDebugCorrelationIds, type WorkbenchDebugFakeSseQueue, type WorkbenchDebugFakeSseSequence, type WorkbenchDebugFakeSseStream, type WorkbenchKafkaSseDebugEvent, type WorkbenchKafkaSseDebugStream, type WorkbenchKafkaSseDebugStreamName } from "@/api";
import StatusBadge from "@/components/common/StatusBadge.vue";
import WorkbenchMessageCard from "@/components/workbench/WorkbenchMessageCard.vue";
import type { WorkbenchRealtimeEvent } from "@/api/workbench-events";
import { applyWorkbenchDebugFakeSseEvent, createWorkbenchDebugFakeSseState } from "@/stores/workbench-debug-fake-sse";

const queueId = "trace-card";
type CanonicalKafkaStreamName = Exclude<WorkbenchKafkaSseDebugStreamName, "hwlab-debug">;
const activeTab = ref<"fake" | "kafka">("fake");
const selectedSequenceId = ref("trace-card-basic");
const sequences = ref<WorkbenchDebugFakeSseSequence[]>([]);
const queue = ref<WorkbenchDebugFakeSseQueue | null>(null);
const projection = ref(createWorkbenchDebugFakeSseState());
const streamStatus = ref<"connecting" | "open" | "error" | "closed">("connecting");
const kafkaStatus = ref<"idle" | "connecting" | "open" | "error" | "closed">("idle");
const kafkaStreamName = ref<CanonicalKafkaStreamName>("hwlab");
const kafkaFromBeginning = ref(false);
const kafkaFilters = ref({ traceId: "", sessionId: "", runId: "", commandId: "" });
const kafkaEvents = ref<KafkaRawRow[]>([]);
const kafkaConnection = ref<WorkbenchKafkaSseDebugEvent | null>(null);
const kafkaTraceStatuses = ref<Record<CanonicalKafkaStreamName, KafkaStreamStatus>>(createKafkaStatusMap());
const kafkaTraceEvents = ref<Record<CanonicalKafkaStreamName, KafkaRawRow[]>>(createKafkaEventMap());
const kafkaTraceConnections = ref<Record<CanonicalKafkaStreamName, WorkbenchKafkaSseDebugEvent | null>>(createKafkaConnectionMap());
const busy = ref(false);
const error = ref<string | null>(null);
const appendDraft = ref(defaultAppendDraft());
let stream: WorkbenchDebugFakeSseStream | null = null;
let kafkaStream: WorkbenchKafkaSseDebugStream | null = null;
const kafkaTraceStreams: Partial<Record<CanonicalKafkaStreamName, WorkbenchKafkaSseDebugStream>> = {};

const KAFKA_TRACE_STREAMS: Array<{ name: CanonicalKafkaStreamName; label: string; topic: string }> = [
  { name: "stdio", label: "codex stdio", topic: "codex-stdio.raw.v1" },
  { name: "agentrun", label: "AgentRun event", topic: "agentrun.event.v1" },
  { name: "hwlab", label: "HWLAB event", topic: "hwlab.event.v1" }
];

type KafkaStreamStatus = "idle" | "connecting" | "open" | "error" | "closed";

interface KafkaRawRow {
  id: string;
  eventName: string;
  receivedAt: string;
  payload: WorkbenchKafkaSseDebugEvent;
}

const message = computed(() => projection.value.message);
const logs = computed(() => projection.value.logs);
const queueSummary = computed(() => queue.value ? `${queue.value.cursor}/${queue.value.eventCount}` : "-");
const streamBadgeStatus = computed(() => streamStatus.value === "open" ? "completed" : streamStatus.value === "error" ? "failed" : "running");
const kafkaBadgeStatus = computed(() => kafkaStatus.value === "open" ? "completed" : kafkaStatus.value === "error" ? "failed" : "running");
const kafkaTraceEventCount = computed(() => Object.values(kafkaTraceEvents.value).reduce((total, rows) => total + rows.length, 0));
const kafkaTraceConnectedCount = computed(() => Object.values(kafkaTraceStatuses.value).filter((status) => status === "open").length);
const canStep = computed(() => !busy.value && (queue.value?.remaining ?? 0) > 0);

onMounted(async () => {
  await refreshDescription();
  openStream();
  await resetQueue();
});

onBeforeUnmount(() => {
  stream?.close();
  stream = null;
  kafkaStream?.close();
  kafkaStream = null;
  closeKafkaTraceStreams();
  streamStatus.value = "closed";
  kafkaStatus.value = "closed";
});

async function refreshDescription(): Promise<void> {
  const response = await workbenchDebugAPI.describe(queueId);
  if (!response.ok) {
    error.value = response.error || "Workbench debug fake SSE 后端不可用";
    return;
  }
  sequences.value = response.data?.sequences ?? [];
  queue.value = response.data?.queue ?? null;
  selectedSequenceId.value = queue.value?.sequenceId ?? sequences.value[0]?.sequenceId ?? "trace-card-basic";
}

function openStream(): void {
  stream?.close();
  streamStatus.value = "connecting";
  stream = connectWorkbenchDebugFakeSse({
    queueId,
    onOpen: () => { streamStatus.value = "open"; },
    onError: () => { streamStatus.value = "error"; },
    onEvent: (event, eventName) => {
      if (eventName === "workbench.connected") return;
      projection.value = applyWorkbenchDebugFakeSseEvent(projection.value, event, eventName);
      error.value = projection.value.error;
    }
  });
  if (!stream) streamStatus.value = "error";
}

function openKafkaStream(): void {
  kafkaStream?.close();
  kafkaStatus.value = "connecting";
  kafkaConnection.value = null;
  kafkaStream = connectWorkbenchKafkaSseDebug({
    stream: kafkaStreamName.value,
    fromBeginning: kafkaFromBeginning.value,
    traceId: cleanFilter(kafkaFilters.value.traceId),
    sessionId: cleanFilter(kafkaFilters.value.sessionId),
    runId: cleanFilter(kafkaFilters.value.runId),
    commandId: cleanFilter(kafkaFilters.value.commandId),
    onOpen: () => { kafkaStatus.value = "open"; },
    onError: () => { kafkaStatus.value = "error"; },
    onEvent: (event, eventName) => {
      if (eventName === "hwlab.kafka.connected") {
        kafkaConnection.value = event;
        return;
      }
      if (eventName === "hwlab.kafka.error") {
        kafkaStatus.value = "error";
        error.value = event.error?.message || "Kafka SSE 事件流错误";
      }
      kafkaEvents.value = [{ id: kafkaRowId(event, eventName), eventName, receivedAt: new Date().toISOString(), payload: event }, ...kafkaEvents.value].slice(0, 120);
    }
  });
  if (!kafkaStream) kafkaStatus.value = "error";
}

function closeKafkaStream(): void {
  kafkaStream?.close();
  kafkaStream = null;
  kafkaStatus.value = "closed";
}

function clearKafkaEvents(): void {
  kafkaEvents.value = [];
}

function openKafkaTraceStreams(): void {
  closeKafkaTraceStreams();
  kafkaTraceEvents.value = createKafkaEventMap();
  kafkaTraceConnections.value = createKafkaConnectionMap();
  kafkaTraceStatuses.value = createKafkaStatusMap("connecting");
  for (const streamName of KAFKA_TRACE_STREAMS.map((entry) => entry.name)) {
    const opened = connectWorkbenchKafkaSseDebug({
      stream: streamName,
      fromBeginning: true,
      traceId: cleanFilter(kafkaFilters.value.traceId),
      sessionId: cleanFilter(kafkaFilters.value.sessionId),
      runId: cleanFilter(kafkaFilters.value.runId),
      commandId: cleanFilter(kafkaFilters.value.commandId),
      onOpen: () => setKafkaTraceStatus(streamName, "open"),
      onError: () => setKafkaTraceStatus(streamName, "error"),
      onEvent: (event, eventName) => {
        if (eventName === "hwlab.kafka.connected") {
          kafkaTraceConnections.value = { ...kafkaTraceConnections.value, [streamName]: event };
          return;
        }
        if (eventName === "hwlab.kafka.error") {
          setKafkaTraceStatus(streamName, "error");
          error.value = event.error?.message || "Kafka SSE 三流订阅错误";
        }
        pushKafkaTraceEvent(streamName, { id: kafkaRowId(event, eventName), eventName, receivedAt: new Date().toISOString(), payload: event });
      }
    });
    if (opened) kafkaTraceStreams[streamName] = opened;
    else setKafkaTraceStatus(streamName, "error");
  }
}

function closeKafkaTraceStreams(): void {
  for (const streamName of KAFKA_TRACE_STREAMS.map((entry) => entry.name)) {
    kafkaTraceStreams[streamName]?.close();
    delete kafkaTraceStreams[streamName];
  }
  kafkaTraceStatuses.value = Object.fromEntries(KAFKA_TRACE_STREAMS.map((entry) => [entry.name, kafkaTraceStatuses.value[entry.name] === "idle" ? "idle" : "closed"])) as Record<CanonicalKafkaStreamName, KafkaStreamStatus>;
}

function clearKafkaTraceEvents(): void {
  kafkaTraceEvents.value = createKafkaEventMap();
}

function setKafkaTraceStatus(streamName: CanonicalKafkaStreamName, status: KafkaStreamStatus): void {
  kafkaTraceStatuses.value = { ...kafkaTraceStatuses.value, [streamName]: status };
}

function pushKafkaTraceEvent(streamName: CanonicalKafkaStreamName, row: KafkaRawRow): void {
  kafkaTraceEvents.value = { ...kafkaTraceEvents.value, [streamName]: [row, ...kafkaTraceEvents.value[streamName]].slice(0, 80) };
}

function createKafkaStatusMap(status: KafkaStreamStatus = "idle"): Record<CanonicalKafkaStreamName, KafkaStreamStatus> {
  return { stdio: status, agentrun: status, hwlab: status };
}

function createKafkaEventMap(): Record<CanonicalKafkaStreamName, KafkaRawRow[]> {
  return { stdio: [], agentrun: [], hwlab: [] };
}

function createKafkaConnectionMap(): Record<CanonicalKafkaStreamName, WorkbenchKafkaSseDebugEvent | null> {
  return { stdio: null, agentrun: null, hwlab: null };
}

async function resetQueue(): Promise<void> {
  busy.value = true;
  error.value = null;
  projection.value = createWorkbenchDebugFakeSseState();
  try {
    const response = await workbenchDebugAPI.reset({ queueId, sequenceId: selectedSequenceId.value });
    if (!response.ok) throw new Error(response.error || "reset failed");
    queue.value = response.data?.queue ?? queue.value;
  } catch (err) {
    error.value = err instanceof Error ? err.message : String(err);
  } finally {
    busy.value = false;
  }
}

async function stepNext(): Promise<void> {
  await controlQueue(() => workbenchDebugAPI.next({ queueId }));
}

async function runAll(): Promise<void> {
  await controlQueue(() => workbenchDebugAPI.run({ queueId }));
}

async function appendDraftEvents(): Promise<void> {
  error.value = null;
  const parsed = parseAppendDraft(appendDraft.value);
  if (!parsed.ok) {
    error.value = parsed.error;
    return;
  }
  await controlQueue(() => workbenchDebugAPI.append({ queueId, events: parsed.events }));
}

async function controlQueue(operation: () => ReturnType<typeof workbenchDebugAPI.next>): Promise<void> {
  busy.value = true;
  error.value = null;
  try {
    const response = await operation();
    if (!response.ok) throw new Error(response.error || "queue operation failed");
    queue.value = response.data?.queue ?? queue.value;
  } catch (err) {
    error.value = err instanceof Error ? err.message : String(err);
  } finally {
    busy.value = false;
  }
}

function parseAppendDraft(raw: string): { ok: true; events: WorkbenchRealtimeEvent[] } | { ok: false; error: string } {
  try {
    const value = JSON.parse(raw);
    const events = Array.isArray(value) ? value : Array.isArray(value?.events) ? value.events : [value];
    const normalized = events.filter((event: unknown): event is WorkbenchRealtimeEvent => Boolean(event && typeof event === "object" && !Array.isArray(event)));
    return normalized.length > 0 ? { ok: true, events: normalized } : { ok: false, error: "JSON 中没有可追加的 event 对象" };
  } catch (err) {
    return { ok: false, error: err instanceof Error ? err.message : "JSON 解析失败" };
  }
}

function cleanFilter(value: string): string | undefined {
  const text = value.trim();
  return text ? text : undefined;
}

function kafkaRowId(event: WorkbenchKafkaSseDebugEvent, eventName: string): string {
  const topic = event.topic || "topic";
  const partition = event.partition ?? "p";
  const offset = event.offset || Date.now();
  return `${eventName}:${topic}:${partition}:${offset}:${Math.random().toString(16).slice(2)}`;
}

function kafkaEventSummary(event: WorkbenchKafkaSseDebugEvent): string {
  const value = recordValue(event.value);
  const nestedEvent = recordValue(value.event);
  return String(value.eventType || value.type || nestedEvent.type || event.error?.code || "event");
}

function kafkaEventIds(event: WorkbenchKafkaSseDebugEvent): string {
  return workbenchKafkaSseDebugCorrelationIds(event).join(" · ") || "-";
}

function kafkaEventJson(event: WorkbenchKafkaSseDebugEvent): string {
  return JSON.stringify(event.value ?? event, null, 2);
}

function recordValue(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
}

function defaultAppendDraft(): string {
  return JSON.stringify({
    type: "trace.event",
    traceId: "trc_debug_fake_sse",
    sessionId: "ses_debug_fake_sse",
    threadId: "thr_debug_fake_sse",
    event: {
      traceId: "trc_debug_fake_sse",
      projectedSeq: 99,
      createdAt: "2026-07-09T08:00:09.000Z",
      label: "assistant:message",
      type: "assistant_message",
      status: "running",
      message: "这是前端临时追加到后端队列的 fake SSE 事件。",
      elapsedMs: 9000
    }
  }, null, 2);
}
</script>

<template>
  <section class="workbench-debug-route">
    <header class="workbench-debug-header">
      <div>
        <p class="page-eyebrow">Workbench Debug</p>
        <h1>实时调试台</h1>
      </div>
      <div class="workbench-debug-status">
        <StatusBadge v-if="activeTab === 'fake'" :status="streamBadgeStatus" :label="`SSE ${streamStatus}`" />
        <StatusBadge v-else :status="kafkaBadgeStatus" :label="`Kafka SSE ${kafkaStatus}`" />
        <span class="debug-counter">{{ activeTab === 'fake' ? `Queue ${queueSummary}` : `${kafkaTraceConnectedCount}/3 streams · ${kafkaTraceEventCount + kafkaEvents.length} events` }}</span>
      </div>
    </header>

    <nav class="workbench-debug-tabs" aria-label="Workbench 调试子标签">
      <button class="debug-tab" :data-active="activeTab === 'fake' ? 'true' : 'false'" type="button" @click="activeTab = 'fake'">SSE Trace</button>
      <button class="debug-tab" :data-active="activeTab === 'kafka' ? 'true' : 'false'" type="button" @click="activeTab = 'kafka'">Kafka SSE</button>
    </nav>

    <section v-if="activeTab === 'fake'" class="workbench-debug-toolbar" aria-label="fake SSE 控制">
      <label>
        <span>序列</span>
        <select v-model="selectedSequenceId" class="input" :disabled="busy" @change="resetQueue">
          <option v-for="sequence in sequences" :key="sequence.sequenceId" :value="sequence.sequenceId">{{ sequence.label }} · {{ sequence.eventCount }}</option>
        </select>
      </label>
      <button class="btn btn-secondary" type="button" :disabled="busy" @click="resetQueue">重置</button>
      <button class="btn btn-primary" type="button" :disabled="!canStep" @click="stepNext">单步发送</button>
      <button class="btn btn-secondary" type="button" :disabled="!canStep" @click="runAll">全速运行</button>
    </section>

    <section v-else class="workbench-debug-toolbar kafka-toolbar" aria-label="Kafka SSE 控制">
      <label class="compact-label">
        <span>流</span>
        <select v-model="kafkaStreamName" class="input" :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'">
          <option value="stdio">codex-stdio.raw.v1</option>
          <option value="hwlab">hwlab.event.v1</option>
          <option value="agentrun">agentrun.event.v1</option>
        </select>
      </label>
      <label>
        <span>traceId</span>
        <input v-model="kafkaFilters.traceId" class="input" placeholder="trc_..." :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" />
      </label>
      <label>
        <span>sessionId</span>
        <input v-model="kafkaFilters.sessionId" class="input" placeholder="ses_..." :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" />
      </label>
      <label class="compact-label">
        <span>runId</span>
        <input v-model="kafkaFilters.runId" class="input" placeholder="run_..." :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" />
      </label>
      <label class="compact-label">
        <span>commandId</span>
        <input v-model="kafkaFilters.commandId" class="input" placeholder="cmd_..." :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" />
      </label>
      <label class="debug-check">
        <input v-model="kafkaFromBeginning" type="checkbox" :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" />
        <span>from beginning</span>
      </label>
      <button class="btn btn-primary" type="button" :disabled="kafkaStatus === 'connecting' || kafkaStatus === 'open'" @click="openKafkaStream">连接</button>
      <button class="btn btn-secondary" type="button" :disabled="kafkaStatus !== 'connecting' && kafkaStatus !== 'open'" @click="closeKafkaStream">断开</button>
      <button class="btn btn-secondary" type="button" @click="clearKafkaEvents">清空</button>
      <button class="btn btn-primary" type="button" :disabled="!cleanFilter(kafkaFilters.traceId)" @click="openKafkaTraceStreams">连接三流</button>
      <button class="btn btn-secondary" type="button" @click="closeKafkaTraceStreams">断开三流</button>
      <button class="btn btn-secondary" type="button" @click="clearKafkaTraceEvents">清空三流</button>
    </section>

    <p v-if="error" class="workbench-debug-error">{{ error }}</p>

    <div v-if="activeTab === 'fake'" class="workbench-debug-grid">
      <section class="debug-surface" aria-label="Trace 卡片投影">
        <WorkbenchMessageCard v-if="message" :message="message" :detail-enabled="false" :trace-auto-expanded="true" storage-key-prefix="hwlab.workbench.debug.trace-open" />
        <div v-else class="debug-empty">等待第一条 fake SSE message.snapshot</div>
      </section>

      <aside class="debug-side">
        <section class="debug-append" aria-label="追加 fake SSE">
          <div class="debug-section-title">
            <strong>Append</strong>
            <button class="btn btn-sm btn-secondary" type="button" :disabled="busy" @click="appendDraftEvents">追加到后端队列</button>
          </div>
          <textarea v-model="appendDraft" class="debug-json-input" spellcheck="false" />
        </section>

        <section class="debug-log" aria-label="SSE 决策日志">
          <div class="debug-section-title">
            <strong>Reducer</strong>
            <span>{{ logs.length }} events</span>
          </div>
          <ol>
            <li v-for="entry in logs" :key="entry.id" :data-accepted="entry.accepted ? 'true' : 'false'">
              <code>{{ entry.eventType }}</code>
              <span>{{ entry.actionType }}</span>
              <small>{{ entry.stepTypes.join(", ") || entry.reason || "no-op" }}</small>
            </li>
          </ol>
        </section>
      </aside>
    </div>

    <section v-else class="kafka-debug-layout" aria-label="Kafka SSE 调试面板">
      <section class="kafka-trace-panel" aria-label="按 traceId 订阅三条 Kafka 流">
        <div class="debug-section-title">
          <strong>Trace Streams</strong>
          <span>{{ kafkaTraceConnectedCount }}/3 open · {{ kafkaTraceEventCount }} events</span>
        </div>
        <div class="kafka-stream-grid">
          <article v-for="streamEntry in KAFKA_TRACE_STREAMS" :key="streamEntry.name" class="kafka-stream-column" :data-stream="streamEntry.name">
            <header class="kafka-stream-head">
              <div>
                <strong>{{ streamEntry.label }}</strong>
                <code>{{ streamEntry.topic }}</code>
                <small v-if="kafkaTraceConnections[streamEntry.name]?.consumerReady" class="kafka-consumer-ready">consumer ready · {{ kafkaTraceConnections[streamEntry.name]?.groupId || '-' }}</small>
              </div>
              <StatusBadge :status="kafkaTraceStatuses[streamEntry.name] === 'open' ? 'completed' : kafkaTraceStatuses[streamEntry.name] === 'error' ? 'failed' : 'running'" :label="kafkaTraceStatuses[streamEntry.name]" />
            </header>
            <div v-if="kafkaTraceEvents[streamEntry.name].length === 0" class="debug-empty">等待匹配 traceId 的 {{ streamEntry.topic }} 事件。</div>
            <ol v-else class="kafka-event-list compact-kafka-event-list">
              <li v-for="row in kafkaTraceEvents[streamEntry.name]" :key="row.id">
                <div class="kafka-event-head">
                  <code>{{ row.eventName }}</code>
                  <strong>{{ kafkaEventSummary(row.payload) }}</strong>
                  <span>#{{ row.payload.offset || '-' }}</span>
                </div>
                <div class="kafka-event-meta">
                  <span>{{ kafkaEventIds(row.payload) }}</span>
                  <time>{{ row.receivedAt }}</time>
                </div>
                <pre>{{ kafkaEventJson(row.payload) }}</pre>
              </li>
            </ol>
          </article>
        </div>
      </section>

      <section class="kafka-debug-panel" aria-label="Kafka SSE 原始事件列表">
      <div class="debug-section-title">
        <strong>Raw SSE Events</strong>
        <span v-if="kafkaConnection?.consumerReady">consumer ready · {{ kafkaConnection.groupId || '-' }} · {{ kafkaEvents.length }} events</span>
        <span v-else>{{ kafkaEvents.length }} events</span>
      </div>
      <div v-if="kafkaEvents.length === 0" class="debug-empty">等待后端从 Kafka 订阅并透传 hwlab event。</div>
      <ol v-else class="kafka-event-list">
        <li v-for="row in kafkaEvents" :key="row.id">
          <div class="kafka-event-head">
            <code>{{ row.eventName }}</code>
            <strong>{{ kafkaEventSummary(row.payload) }}</strong>
            <span>{{ row.payload.topic || '-' }} #{{ row.payload.offset || '-' }}</span>
          </div>
          <div class="kafka-event-meta">
            <span>{{ kafkaEventIds(row.payload) }}</span>
            <time>{{ row.receivedAt }}</time>
          </div>
          <pre>{{ kafkaEventJson(row.payload) }}</pre>
        </li>
      </ol>
      </section>
    </section>
  </section>
</template>

<style scoped>
.workbench-debug-route {
  display: grid;
  min-height: 100%;
  grid-template-rows: auto auto auto auto minmax(0, 1fr);
  gap: 12px;
  color: #111827;
}

.workbench-debug-header,
.workbench-debug-toolbar,
.debug-section-title,
.workbench-debug-status {
  display: flex;
  align-items: center;
}

.workbench-debug-header {
  justify-content: space-between;
  gap: 16px;
  border-bottom: 1px solid #d8e1eb;
  padding: 4px 2px 12px;
}

.workbench-debug-header h1 {
  margin: 0;
  font-size: 24px;
  line-height: 1.1;
}

.workbench-debug-status {
  gap: 10px;
}

.debug-counter {
  border: 1px solid #d1d5db;
  border-radius: 8px;
  background: white;
  padding: 7px 10px;
  color: #475569;
  font-size: 12px;
  font-weight: 750;
}

.workbench-debug-tabs {
  display: flex;
  gap: 8px;
  border-bottom: 1px solid #d8e1eb;
}

.debug-tab {
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  padding: 10px 12px;
  color: #475569;
  font-size: 13px;
  font-weight: 800;
}

.debug-tab[data-active="true"] {
  border-bottom-color: #0e7490;
  color: #0f172a;
}

.workbench-debug-toolbar {
  flex-wrap: wrap;
  gap: 10px;
}

.workbench-debug-toolbar label {
  display: grid;
  width: min(360px, 100%);
  gap: 4px;
  color: #64748b;
  font-size: 12px;
  font-weight: 750;
}

.workbench-debug-toolbar .compact-label {
  width: min(220px, 100%);
}

.kafka-toolbar {
  align-items: end;
}

.debug-check {
  display: flex !important;
  width: auto !important;
  grid-auto-flow: column;
  align-items: center;
  gap: 7px !important;
  padding-bottom: 8px;
  color: #334155 !important;
}

.workbench-debug-grid {
  display: grid;
  min-height: 0;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
  gap: 12px;
}

.debug-surface,
.debug-side,
.debug-append,
.debug-log {
  min-width: 0;
  min-height: 0;
}

.debug-surface {
  overflow: auto;
  border: 1px solid #d8e1eb;
  border-radius: 8px;
  background: #f8fafc;
  padding: 12px;
}

.debug-empty,
.workbench-debug-error {
  border-radius: 8px;
  padding: 12px;
}

.debug-empty {
  border: 1px dashed #cbd5e1;
  background: white;
  color: #64748b;
  font-size: 13px;
}

.workbench-debug-error {
  border: 1px solid #fecaca;
  background: #fff1f2;
  color: #9f1239;
  font-size: 13px;
}

.debug-side {
  display: grid;
  grid-template-rows: minmax(240px, 1fr) minmax(160px, 0.8fr);
  gap: 12px;
}

.debug-append,
.debug-log {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
  border: 1px solid #d8e1eb;
  border-radius: 8px;
  background: white;
  padding: 10px;
}

.debug-section-title {
  justify-content: space-between;
  gap: 10px;
  color: #334155;
}

.debug-section-title span {
  color: #64748b;
  font-size: 12px;
}

.debug-json-input {
  min-height: 0;
  resize: none;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  background: #0f172a;
  padding: 10px;
  color: #d1fae5;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  line-height: 1.45;
}

.debug-log ol {
  display: grid;
  align-content: start;
  gap: 6px;
  min-height: 0;
  margin: 0;
  overflow: auto;
  padding: 0;
  list-style: none;
}

.debug-log li {
  display: grid;
  grid-template-columns: minmax(96px, 0.7fr) minmax(96px, 0.7fr) minmax(0, 1fr);
  gap: 8px;
  border-left: 3px solid #22c55e;
  background: #f8fafc;
  padding: 8px;
  color: #334155;
  font-size: 12px;
}

.debug-log li[data-accepted="false"] {
  border-left-color: #ef4444;
}

.debug-log code {
  color: #0f766e;
  white-space: nowrap;
}

.debug-log small {
  overflow: hidden;
  color: #64748b;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kafka-debug-layout {
  display: grid;
  min-height: 0;
  grid-template-rows: minmax(280px, 0.9fr) minmax(220px, 0.7fr);
  gap: 12px;
}

.kafka-trace-panel {
  display: grid;
  min-height: 0;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 10px;
}

.kafka-stream-grid {
  display: grid;
  min-height: 0;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
}

.kafka-stream-column {
  display: grid;
  min-width: 0;
  min-height: 0;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
  border: 1px solid #d8e1eb;
  border-radius: 8px;
  background: white;
  padding: 10px;
}

.kafka-stream-head {
  display: flex;
  align-items: start;
  justify-content: space-between;
  gap: 10px;
}

.kafka-stream-head div {
  display: grid;
  min-width: 0;
  gap: 3px;
}

.kafka-stream-head strong {
  color: #0f172a;
  font-size: 13px;
}

.kafka-stream-head code {
  overflow: hidden;
  color: #0f766e;
  font-size: 11px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kafka-consumer-ready {
  overflow: hidden;
  color: #047857;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 10px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.kafka-debug-panel {
  display: grid;
  min-height: 0;
  grid-template-rows: auto minmax(0, 1fr);
  gap: 10px;
  border: 1px solid #d8e1eb;
  border-radius: 8px;
  background: white;
  padding: 10px;
}

.kafka-event-list {
  display: grid;
  align-content: start;
  gap: 8px;
  min-height: 0;
  margin: 0;
  overflow: auto;
  padding: 0;
  list-style: none;
}

.kafka-event-list li {
  display: grid;
  gap: 6px;
  border: 1px solid #d8e1eb;
  border-left: 3px solid #0e7490;
  border-radius: 8px;
  background: #f8fafc;
  padding: 9px;
}

.kafka-event-head,
.kafka-event-meta {
  display: grid;
  grid-template-columns: minmax(150px, 0.5fr) minmax(160px, 0.7fr) minmax(0, 1fr);
  gap: 10px;
  align-items: center;
  color: #334155;
  font-size: 12px;
}

.kafka-event-head code {
  color: #0f766e;
}

.kafka-event-head span,
.kafka-event-meta {
  color: #64748b;
}

.kafka-event-meta {
  grid-template-columns: minmax(0, 1fr) auto;
}

.kafka-event-list pre {
  max-height: 220px;
  margin: 0;
  overflow: auto;
  border-radius: 7px;
  background: #0f172a;
  padding: 9px;
  color: #d1fae5;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 11px;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.compact-kafka-event-list .kafka-event-head {
  grid-template-columns: minmax(92px, 0.6fr) minmax(110px, 0.8fr) minmax(44px, 0.4fr);
}

.compact-kafka-event-list .kafka-event-meta {
  grid-template-columns: minmax(0, 1fr);
}

.compact-kafka-event-list .kafka-event-meta time {
  display: none;
}

.compact-kafka-event-list pre {
  max-height: 160px;
}

@media (max-width: 980px) {
  .workbench-debug-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .kafka-debug-layout {
    grid-template-rows: auto auto;
  }

  .kafka-stream-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .debug-side {
    grid-template-rows: 280px 220px;
  }
}
</style>
