// src/layer/adapters/index.ts
//
// AdapterRegistry — ordered registry of ToolResultAdapters.
//
// Dispatch walks adapters top-to-bottom and returns the first whose match()
// returns true (and whose compress() does not throw). A throwing adapter is
// treated as not-matched (correctness > coverage) so an over-eager codec
// cannot corrupt a tool_result: we just fall through to the next adapter.
//
// The passthrough adapter MUST be registered last by callers — it is the
// safety net that guarantees every tool_result has at least one match. If
// dispatch() reaches the end of the list with no match, it throws (which
// signals a wiring bug in the Wave-3 registerTier1 path, not a runtime
// data problem).
//
// See ./types.ts for the contract and load-bearing invariants (Rule A:
// frozen bytes; Rule B: latest-turn only; determinism).

import type { ToolResult } from "../../provider/canonical.ts";
import type { ToolResultAdapter, AdapterDispatchResult } from "./types.ts";

export type {
  ToolResultAdapter,
  AdapterDispatchResult,
  CompressedResult,
} from "./types.ts";

export class AdapterRegistry {
  private adapters: ToolResultAdapter[] = [];

  register(adapter: ToolResultAdapter): void {
    this.adapters.push(adapter);
  }

  dispatch(r: ToolResult): AdapterDispatchResult {
    // ToolResult.content is typed as `string` in src/provider/canonical.ts.
    // The conditional JSON.stringify is belt-and-suspenders against future
    // widening of the canonical type — keep it cheap and safe.
    const raw: string =
      typeof r.content === "string" ? r.content : JSON.stringify(r.content);

    for (const a of this.adapters) {
      try {
        if (!a.match(r)) continue;
        const compressed = a.compress(r);
        return { adapterId: a.id, compressed, raw };
      } catch {
        // Adapter threw — treat as not-matched and fall through.
        // Correctness > coverage: a buggy adapter must not corrupt the
        // model's view of a tool_result.
        continue;
      }
    }

    throw new Error(
      `AdapterRegistry: no adapter matched toolCallId=${r.toolCallId} (passthrough not registered?)`,
    );
  }
}
