// Responsibility: pure, isolated projection of hwlab.event.debug.v1 records through the production Workbench reducers.
// This module never reads or writes the canonical Workbench store.

import type { WorkbenchKafkaSseDebugEvent } from "@/api/workbench-debug";
import type { WorkbenchRealtimeEvent } from "@/api/workbench-events";
import type { ChatMessage } from "@/types";
import { firstNonEmptyString } from "@/utils";
import { reduceWorkbenchRealtimeEvent } from "./workbench-event-reducer";
import { projectWorkbenchLiveKafkaMessage } from "./workbench-live-kafka-event";
import { planWorkbenchRealtimeApply } from "./workbench-realtime-plan";

export interface WorkbenchIsolatedKafkaDebugLog {
  id: string;
  eventType: string;
  actionType: string;
  stepTypes: string[];
  traceId: string | null;
  accepted: boolean;
  reason: string | null;
  observedAt: string;
}

export interface WorkbenchIsolatedKafkaDebugState {
  message: ChatMessage | null;
  receivedCount: number;
  appliedCount: number;
  lastOffset: string | null;
  logs: WorkbenchIsolatedKafkaDebugLog[];
  error: string | null;
}

export function createWorkbenchIsolatedKafkaDebugState(): WorkbenchIsolatedKafkaDebugState {
  return { message: null, receivedCount: 0, appliedCount: 0, lastOffset: null, logs: [], error: null };
}

export function applyWorkbenchIsolatedKafkaDebugEvent(
  state: WorkbenchIsolatedKafkaDebugState,
  envelope: WorkbenchKafkaSseDebugEvent,
  expectedTraceId: string | null | undefined
): WorkbenchIsolatedKafkaDebugState {
  const value = recordValue(envelope.value);
  const debugEvent = value as WorkbenchRealtimeEvent;
  const traceId = realtimeTraceId(debugEvent);
  const expected = firstNonEmptyString(expectedTraceId);
  const receivedCount = state.receivedCount + 1;
  const base = { ...state, receivedCount, lastOffset: firstNonEmptyString(envelope.offset, state.lastOffset) ?? null };
  if (debugEvent.schema !== "hwlab.event.debug.v1") return rejected(base, debugEvent, traceId, "debug-envelope-schema-mismatch");
  if (!traceId) return rejected(base, debugEvent, null, "debug-envelope-trace-missing");
  if (expected && traceId !== expected) return rejected(base, debugEvent, traceId, "debug-envelope-trace-mismatch");

  const event = { ...debugEvent, schema: "hwlab.event.v1" } as WorkbenchRealtimeEvent;
  const reduced = reduceWorkbenchRealtimeEvent(event, "hwlab.event.v1");
  const plan = planWorkbenchRealtimeApply(reduced.action);
  const traceStep = plan.steps.find((step) => step.type === "apply-trace-event");
  if (!traceStep || traceStep.type !== "apply-trace-event" || !traceStep.event) {
    return withLog(base, event, traceId, reduced.action.type, plan.steps.map((step) => step.type), false, reduced.action.type === "ignore" ? reduced.action.reason : "debug-envelope-not-a-trace-event");
  }
  const message = projectWorkbenchLiveKafkaMessage({
    previous: state.message,
    traceId,
    sessionId: firstNonEmptyString(event.hwlabSessionId, event.sessionId, traceStep.event.sessionId, state.message?.sessionId) ?? "ses_workbench_isolated_debug",
    event: traceStep.event,
    receivedAt: new Date().toISOString(),
    title: "Code Agent · 隔离调试"
  });
  return withLog({ ...base, message, appliedCount: state.appliedCount + 1, error: null }, event, traceId, reduced.action.type, plan.steps.map((step) => step.type), true, null);
}

export function workbenchCurrentDebugTraceId(messages: ChatMessage[], sessionLastTraceId?: string | null): string | null {
  const agentMessages = messages.filter((message) => message.role === "agent");
  const running = [...agentMessages].reverse().find((message) => ["pending", "running"].includes(String(message.status ?? "").trim().toLowerCase()) && firstNonEmptyString(message.traceId, message.runnerTrace?.traceId));
  return firstNonEmptyString(
    running?.traceId,
    running?.runnerTrace?.traceId,
    ...[...agentMessages].reverse().flatMap((message) => [message.traceId, message.runnerTrace?.traceId]),
    sessionLastTraceId
  ) ?? null;
}

function rejected(state: WorkbenchIsolatedKafkaDebugState, event: WorkbenchRealtimeEvent, traceId: string | null, reason: string): WorkbenchIsolatedKafkaDebugState {
  return withLog(state, event, traceId, "ignore", [], false, reason);
}

function withLog(
  state: WorkbenchIsolatedKafkaDebugState,
  event: WorkbenchRealtimeEvent,
  traceId: string | null,
  actionType: string,
  stepTypes: string[],
  accepted: boolean,
  reason: string | null
): WorkbenchIsolatedKafkaDebugState {
  const observedAt = new Date().toISOString();
  const log: WorkbenchIsolatedKafkaDebugLog = {
    id: `${observedAt}:${state.receivedCount}:${traceId ?? "none"}`,
    eventType: firstNonEmptyString(event.eventType, event.event?.type, event.type, "unknown") ?? "unknown",
    actionType,
    stepTypes,
    traceId,
    accepted,
    reason,
    observedAt
  };
  return { ...state, error: accepted ? null : reason, logs: [log, ...state.logs].slice(0, 40) };
}

function realtimeTraceId(event: WorkbenchRealtimeEvent): string | null {
  return firstNonEmptyString(event.traceId, event.event?.traceId) ?? null;
}

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