/**
 * PipelineContext — mutable state bag threaded through all pipeline stages.
 *
 * Each stage reads its inputs from the context and writes its outputs back.
 * The context is created fresh per request and is never shared across requests.
 *
 * ## Data ownership
 *
 * Stages must write to non-overlapping fields. For parallel execution, this
 * means each `rag-query` handler writes to its own store slot
 * in `ragResults`, avoiding data races.
 *
 * ## Streaming
 *
 * The `tool-loop` stage streams. It uses `ctx.yield()` to push
 * SSE chunks back to the caller. All other stages are batch operations.
 */

import type {
  CallOptions,
  IContextAssembler,
  IEmbedder,
  IHistoryMemory,
  IHistorySummarizer,
  ILlm,
  ILlmCallStrategy,
  ILogger,
  IMcpClient,
  IQueryEmbedding,
  IQueryExpander,
  IRagProviderRegistry,
  IRagRegistry,
  IRequestLogger,
  ISkill,
  ISkillManager,
  ISubpromptClassifier,
  IToolCache,
  IToolSelectionStrategy,
  LlmStreamChunk,
  LlmTool,
  McpTool,
  Message,
  OnPartial,
  Plan,
  RagResult,
  Result,
  StepResult,
  SubAgentRegistry,
  Subprompt,
  TimingEntry,
} from '@mcp-abap-adt/llm-agent';
import type {
  OrchestratorError,
  SmartAgentConfig,
  SmartAgentRagStores,
} from '../agent.js';
import type { IMetrics } from '../metrics/types.js';
import type { PendingToolResultsRegistry } from '../policy/pending-tool-results-registry.js';
import type { ToolAvailabilityRegistry } from '../policy/tool-availability-registry.js';
import type { IPromptInjectionDetector, IToolPolicy } from '../policy/types.js';
import type { IReranker } from '../reranker/types.js';
import type { ISessionManager } from '../session/types.js';
import type { ITracer } from '../tracer/types.js';
import type { IOutputValidator } from '../validator/types.js';

// ---------------------------------------------------------------------------
// Pipeline context
// ---------------------------------------------------------------------------

export interface PipelineContext {
  // -- Immutable input (set once at creation, never modified) ----------------

  /** Original user input (string or message array). */
  readonly textOrMessages: string | Message[];
  /** Call options including signal, sessionId, logger, trace. */
  readonly options: CallOptions | undefined;
  /** SmartAgent config snapshot for this request. */
  readonly config: SmartAgentConfig;
  /** Session ID (from options or 'default'). */
  readonly sessionId: string;

  // -- Dependencies (injected at creation, read-only) -----------------------

  readonly mainLlm: ILlm;
  readonly helperLlm: ILlm | undefined;
  readonly classifierLlm: ILlm;
  readonly classifier: ISubpromptClassifier;
  readonly assembler: IContextAssembler;
  readonly ragStores: SmartAgentRagStores;
  readonly ragRegistry: IRagRegistry | undefined;
  readonly ragProviderRegistry: IRagProviderRegistry | undefined;
  readonly mcpClients: IMcpClient[];
  readonly reranker: IReranker;
  readonly queryExpander: IQueryExpander;
  readonly toolCache: IToolCache;
  readonly outputValidator: IOutputValidator;
  readonly sessionManager: ISessionManager;
  readonly tracer: ITracer;
  readonly metrics: IMetrics;
  readonly logger: ILogger | undefined;
  readonly requestLogger: IRequestLogger;
  readonly toolPolicy: IToolPolicy | undefined;
  readonly injectionDetector: IPromptInjectionDetector | undefined;
  readonly toolAvailabilityRegistry: ToolAvailabilityRegistry;
  readonly pendingToolResults: PendingToolResultsRegistry;
  readonly skillManager: ISkillManager | undefined;
  readonly embedder: IEmbedder | undefined;
  readonly toolSelectionStrategy: IToolSelectionStrategy | undefined;
  readonly historyMemory: IHistoryMemory | undefined;
  readonly historySummarizer: IHistorySummarizer | undefined;
  readonly llmCallStrategy: ILlmCallStrategy;

  // -- Mutable state (populated by stages) ----------------------------------

  /** Extracted user text from the last user message. */
  inputText: string;
  /** Conversation history (may be summarized). */
  history: Message[];
  /** Classified subprompts (set by classify stage). */
  subprompts: Subprompt[];
  /** Map of tool name → owning MCP client. */
  toolClientMap: Map<string, IMcpClient>;
  /** Text used for RAG queries (may be translated/expanded). */
  ragText: string;
  /**
   * Enriched query text for tool/skill retrieval, composed by the
   * `build-tool-query` stage from `ragText` + top-K RAG snippets +
   * selected skill descriptions. When present, stages configured with
   * `queryText: 'toolQueryText'` use this instead of `ragText`.
   */
  toolQueryText?: string;
  /** Memoized query embedding, shared across all rag-query stages. */
  queryEmbedding: IQueryEmbedding | undefined;
  /** RAG query results per store. */
  ragResults: Record<string, RagResult[]>;
  /** All MCP tools from all connected servers. */
  mcpTools: McpTool[];
  /** Tools selected for the current request (MCP + external). */
  selectedTools: LlmTool[];
  /** External tools provided by the client. */
  externalTools: LlmTool[];
  /**
   * Results for previously-surfaced external (client-executed) tool calls,
   * keyed by deterministic `externalToolCallId(name, args)` (spec D1). Built by
   * the coordinator from the incoming history on a stateless resume. When the
   * worker LLM re-emits an external call whose `extId` is present here, the
   * tool-loop injects the result as a matched assistant→tool pair and CONTINUES
   * instead of re-surfacing it. Absent on a first request.
   */
  externalResults?: Map<string, string>;
  /** Final assembled messages for LLM input. */
  assembledMessages: Message[];
  /** Currently active tools (after availability filtering). */
  activeTools: LlmTool[];
  /** Skills selected for the current request. */
  selectedSkills: ISkill[];
  /** Rendered skill content to inject into the system prompt. */
  skillContent: string;
  /** Arguments passed to skills (e.g. from slash-command invocation). */
  skillArgs: string;

  // -- Control flags (computed by stages, read by conditions) ---------------

  /** Whether RAG retrieval should run (set by classify or condition logic). */
  shouldRetrieve: boolean;
  /** Whether embedding usage has been logged for this request (prevents double-logging). */
  embeddingUsageLogged?: boolean;
  /** Whether input text is ASCII-only (affects translation decision). */
  isAscii: boolean;
  /** Whether SAP/ABAP context was detected. */
  isSapRequired: boolean;

  // -- Output ---------------------------------------------------------------

  /** Timing entries collected from all stages. */
  timing: TimingEntry[];
  /** Error set by a stage to abort the pipeline. */
  error?: OrchestratorError;

  // -- Streaming callback ---------------------------------------------------

  /**
   * Yield a chunk to the consumer. Used by the tool-loop stage to push
   * streaming content and heartbeats back through the SSE connection.
   */
  yield(chunk: Result<LlmStreamChunk, OrchestratorError>): void;

  /**
   * Optional partial-output callback threaded from `ISubAgentInput.onPartial`.
   * When present, the tool-loop stage forwards every content delta and
   * tool-call event as a `StreamChunk` so the calling coordinator can
   * render live progress without polling.
   */
  onPartial?: OnPartial;

  // -- Coordinator / subagent orchestration ----------------------------------

  /** Subagent stage outputs written by SubAgentHandler. Keyed by output path. */
  subResults?: Record<string, unknown>;
  /** Subagent registry exposed to runtime stages (coordinator, subagent). */
  subAgents?: SubAgentRegistry;
  /**
   * Runtime activation flag computed by the `coordinator-activate` stage from
   * the configured IActivationStrategy with REAL ctx state (selectedSkills,
   * subAgents). Coordinator and tool-loop stages gate on this via `when:`
   * predicates so the activation decision honours runtime skill selection.
   */
  coordinatorActive?: boolean;
  /** Coordinator plan, if a coordinator stage ran. */
  plan?: Plan;
  /** Index of the step currently being executed by the coordinator. */
  currentStepIdx?: number;
  /** Per-step results captured by the coordinator, keyed by stepId. */
  stepResults?: Record<string, StepResult>;
}
