// src/layer/pipeline.ts
import type { Layer, SavingEvent } from "./types.ts";
import type { Provider } from "../provider/types.ts";
import { profileLog, pnow } from "../proxy/profile.ts";

export async function initAll(layers: Layer[]): Promise<void> {
  for (const l of layers) await l.init();
}

export async function runOutbound(body: unknown, layers: Layer[]): Promise<unknown> {
  let current = body;
  const tAll = pnow();
  for (const l of layers) {
    const t0 = pnow();
    current = await l.outbound(current);
    profileLog(`outbound.${l.id}`, pnow() - t0);
  }
  profileLog("outbound.TOTAL", pnow() - tAll);
  return current;
}

export async function runInbound(body: unknown, layers: Layer[]): Promise<unknown> {
  let current = body;
  const tAll = pnow();
  for (let i = layers.length - 1; i >= 0; i--) {
    const l = layers[i]!;
    const t0 = pnow();
    current = await l.inbound(current);
    profileLog(`inbound.${l.id}`, pnow() - t0);
  }
  profileLog("inbound.TOTAL", pnow() - tAll);
  return current;
}

export function collectObservations(layers: Layer[]): SavingEvent[] {
  const out: SavingEvent[] = [];
  for (const l of layers) out.push(...l.observe());
  return out;
}

export function disposeAll(layers: Layer[]): void {
  for (const l of layers) l.dispose();
}

export interface Pipeline {
  readonly layers: Layer[];
  outbound(body: unknown): Promise<unknown>;
  inbound(body: unknown): Promise<unknown>;
  observe(): SavingEvent[];
}

export interface PipelineOpts {
  provider?: Provider;
  extraLayers?: Layer[];
}

/**
 * makePipeline: builds the layer stack for a session.
 * ReadCacheLayer is NOT included here — handler creates one per session and
 * prepends it to pipeline.layers per-request.
 */
export async function makePipeline(opts?: PipelineOpts): Promise<Pipeline> {
  const extra = opts?.extraLayers ?? [];
  const layers: Layer[] = [...extra];
  for (const l of layers) await l.init();
  return {
    layers,
    outbound(body: unknown): Promise<unknown> { return runOutbound(body, layers); },
    inbound(body: unknown): Promise<unknown> { return runInbound(body, layers); },
    observe(): SavingEvent[] { return collectObservations(layers); },
  };
}
