// SPEC: pikasTech/HWLAB#2356 Workbench bounded request storm follow-up.
// Responsibility: Pure budgeting helpers for Workbench detail read de-duplication and projection merge writes.

import { firstNonEmptyString } from "@/utils";
import { composeWorkbenchScopedKey } from "@/utils/workbench-key";

export interface SessionMessagesReadKeyInput {
  sessionId: string | null | undefined;
  limit: number | null | undefined;
  force?: boolean | null;
}

export interface SessionDetailReadKeyInput {
  sessionId: string | null | undefined;
  force?: boolean | null;
}

export interface TraceEventsReadRangeInput {
  traceId: string | null | undefined;
  afterProjectedSeq: number | null | undefined;
  limit: number | null | undefined;
}

export interface TraceEventsReadRangeRecord extends TraceEventsReadRangeInput {
  nextProjectedSeq: number | null;
  hasMore: boolean;
}

export interface TraceEventsAutoReadDecisionInput extends TraceEventsReadRangeInput {
  force?: boolean | null;
  terminalBodyVisible?: boolean | null;
  cachedRange?: TraceEventsReadRangeRecord | null;
}

export interface ReadDetailDecision {
  read: boolean;
  reason: string;
}

export function workbenchSessionDetailReadKey(input: SessionDetailReadKeyInput): string {
  return composeWorkbenchScopedKey(
    "workbench.session-detail.read",
    firstNonEmptyString(input.sessionId) ?? "unknown-session",
    "messages:false",
    input.force === true ? "force" : "fresh"
  );
}

export function workbenchSessionMessagesReadKey(input: SessionMessagesReadKeyInput): string {
  return composeWorkbenchScopedKey(
    "workbench.session-messages.read",
    firstNonEmptyString(input.sessionId) ?? "unknown-session",
    sessionMessagesReadLimitPart(input.limit),
    input.force === true ? "force" : "fresh"
  );
}

export function workbenchTraceEventsReadKey(input: TraceEventsReadRangeInput): string {
  return composeWorkbenchScopedKey(
    "workbench.trace-events.read",
    firstNonEmptyString(input.traceId) ?? "unknown-trace",
    `after:${readSeqPart(input.afterProjectedSeq)}`,
    `limit:${sessionMessagesReadLimitPart(input.limit)}`
  );
}

export function traceEventsAutoReadDecision(input: TraceEventsAutoReadDecisionInput): ReadDetailDecision {
  if (input.force === true) return { read: true, reason: "force" };
  if (input.terminalBodyVisible === true) return { read: false, reason: "terminal-visible" };
  const afterProjectedSeq = readSeqPart(input.afterProjectedSeq);
  if (afterProjectedSeq <= 0) return { read: false, reason: "initial-range-deferred" };
  const cached = input.cachedRange;
  if (cached && readSeqPart(cached.afterProjectedSeq) === afterProjectedSeq && sessionMessagesReadLimitPart(cached.limit) === sessionMessagesReadLimitPart(input.limit) && cached.hasMore !== true) return { read: false, reason: "equivalent-range-complete" };
  return { read: true, reason: "delta-range" };
}

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

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