// src/layer/OutputRewriteLayer.ts
//
// Expands §READ: elision markers in inbound SSE so the LLM client sees
// original file content. Does NOT implement Layer (operates on raw Response
// stream, not parsed body) — handler.ts calls wrapResponse() directly.

import { sseRewriteStream } from "../codec/sseRewriteStream.ts";
import type { SavingEvent, FileHash } from "./types.ts";
import type { ReadCache } from "../cache/ReadCache.ts";

export class OutputRewriteLayer {
  readonly id = "output_rewrite";

  private pending: SavingEvent[] = [];

  constructor(private readonly readCache?: ReadCache) {}

  init(): void {
    this.pending = [];
  }

  dispose(): void {
    this.pending = [];
  }

  wrapResponse(res: Response): Response {
    // If no readCache, nothing to expand — pass through.
    if (!this.readCache || res.body === null) return res;

    const pending = this.pending;
    const rc = this.readCache;

    const readCacheExpander = (hash: string): string | null =>
      rc.readContent(hash as FileHash);

    const rewriteStream = sseRewriteStream({
      table: new Map(),
      readCacheExpander,
      onStats: (stats) => {
        pending.push({
          layerId: "output_rewrite",
          kind: "expand",
          rawBytes: 0,
          sentBytes: 0,
          meta: {
            sigils_expanded: stats.sigils_expanded,
            sse_events_touched: stats.sse_events_touched,
            deferred_boundary: stats.deferred_boundary,
          },
        });
      },
    });

    const rewrittenBody = res.body.pipeThrough(rewriteStream);
    const headers = new Headers(res.headers);
    headers.delete("content-length");

    return new Response(rewrittenBody, {
      status: res.status,
      statusText: res.statusText,
      headers,
    });
  }

  observe(): SavingEvent[] {
    const e = this.pending;
    this.pending = [];
    return e;
  }
}
