/**
 * Claude Code (and similar Anthropic SDK-based clients) prepend a synthetic
 * text block to the `system` array that looks like an HTTP header:
 *
 *   x-anthropic-billing-header: cc_version=2.1.92.61c; cc_entrypoint=cli; cch=00000;
 *
 * The `cch` field is a per-request cache-hint value. When this block is
 * forwarded verbatim to a third-party model, the system prompt prefix
 * changes on every request, which kills prefix-cache hit rates and wastes
 * input tokens.
 *
 * This module detects the marker and returns a rewritten body with the
 * block removed. It is opt-in (see constants.ts) so the proxy remains a
 * transparent pass-through by default.
 */

const BILLING_HEADER_PREFIX = "x-anthropic-billing-header:";

type StripResult = {
  /** The new request body. Equal to input when no change was made. */
  body: string;
  /** Number of billing-header text blocks removed. */
  removed: number;
};

/**
 * Returns true if the given text looks like the synthetic billing-header
 * block that Claude Code injects. Whitespace at the start is tolerated and
 * the match is case-insensitive on the header name.
 */
export function isClaudeCodeBillingHeaderBlock(text: string): boolean {
  const trimmed = text.trimStart();
  return trimmed.toLowerCase().startsWith(BILLING_HEADER_PREFIX);
}

import { safeGetOwnProperty, isPlainRecord } from "../lib/objectUtils";

function isObjectWithSystem(value: unknown): value is { system?: unknown[] } {
  if (!isPlainRecord(value)) return false;
  return Object.prototype.hasOwnProperty.call(value, "system");
}

function isBillingHeaderTextBlock(block: unknown): boolean {
  if (!isPlainRecord(block)) return false;
  const typeVal = safeGetOwnProperty(block, "type");
  const textVal = safeGetOwnProperty(block, "text");
  if (typeof typeVal !== "string" || typeVal !== "text") return false;
  if (typeof textVal !== "string") return false;
  return isClaudeCodeBillingHeaderBlock(textVal);
}

/**
 * Inspects the request body and, if it is an Anthropic-format request whose
 * `system` array contains one or more Claude Code billing-header text blocks,
 * returns a new body with those blocks removed. The original string is
 * returned unchanged (by reference equality on the result) when no edit is
 * needed so callers can avoid re-serializing.
 */
export function stripClaudeCodeBillingHeader(rawBody: string): StripResult {
  let parsed: unknown;
  try {
    parsed = JSON.parse(rawBody);
  } catch {
    return { body: rawBody, removed: 0 };
  }

  if (!isObjectWithSystem(parsed)) {
    return { body: rawBody, removed: 0 };
  }

  const system = parsed.system;

  if (!Array.isArray(system) || system.length === 0) {
    return { body: rawBody, removed: 0 };
  }

  const kept: unknown[] = [];
  let removed = 0;

  for (const block of system) {
    if (isBillingHeaderTextBlock(block)) {
      removed += 1;
      continue;
    }
    kept.push(block);
  }

  if (removed === 0) {
    return { body: rawBody, removed: 0 };
  }

  if (kept.length === 0) {
    delete parsed.system;
  } else {
    parsed.system = kept;
  }

  return { body: JSON.stringify(parsed), removed };
}
