// src/proxy/sanitize.ts
import { CTX_TOOL_PREFIX, CTX_CMD_RE, CTX_SKILL_LINE_RE } from "../adapters/contextMode.ts";
import { getLedger } from "../stats/ledger.ts";

const PARITY_COMPLETE = true;

const CTX_TAG_RE = /<context_window_protection>[\s\S]*?<\/context_window_protection>\s*/g;
const CTX_TAG_MAX_LEN = 4 * 1024 * 1024;

function stripCtxTags(text: string): string {
  if (text.length > CTX_TAG_MAX_LEN) return text;
  return text.replace(CTX_TAG_RE, "");
}

interface ContentBlock {
  type?: string;
  text?: string;
  cache_control?: unknown;
  [k: string]: unknown;
}
interface Message {
  role?: string;
  content?: string | ContentBlock[];
}
export interface AnthropicRequest {
  system?: string | ContentBlock[];
  tools?: Array<{ name?: string; [k: string]: unknown }>;
  messages?: Message[];
  [k: string]: unknown;
}

function stripSkillEnum(text: string): string {
  return text.replace(CTX_SKILL_LINE_RE, "");
}

/** Layers 1, 2, 3. Pure — returns new top-level object; never mutates input. */
export function sanitizeRequest(body: AnthropicRequest): AnthropicRequest {
  if (!PARITY_COMPLETE) return body;
  const out: AnthropicRequest = { ...body };

  // Layer 1 + Layer 3 — strip <context_window_protection> and skill enum bullets
  if (typeof body.system === "string") {
    out.system = stripSkillEnum(stripCtxTags(body.system))
      .replace(/[ \t]{2,}/g, " ");
  } else if (Array.isArray(body.system)) {
    out.system = body.system.map((block) => {
      if (block && typeof block === "object" && typeof block.text === "string") {
        let next = stripCtxTags(block.text);
        next = stripSkillEnum(next);
        if (next.replace(/\s+/g, "").length === 0) next = " ";
        return { ...block, text: next };
      }
      return block;
    });
  }

  // Layer 2 — drop ctx-mode MCP tools
  if (Array.isArray(body.tools)) {
    out.tools = body.tools.filter(
      (t) => !(typeof t?.name === "string" && t.name.startsWith(CTX_TOOL_PREFIX)),
    );
  }

  try {
    const bytesIn = JSON.stringify(body).length;
    const bytesOut = JSON.stringify(out).length;
    if (bytesIn > bytesOut) {
      getLedger().record("fewtok-core", { bytesIn, bytesOut, hits: 1 });
    }
  } catch { /* swallow */ }

  return out;
}

/** Layer 5 — scan only the LATEST user-role message. */
export function detectCtxModeCommand(body: AnthropicRequest): boolean {
  if (!PARITY_COMPLETE) return false;
  if (!Array.isArray(body.messages)) return false;
  let lastUser: Message | null = null;
  for (let i = body.messages.length - 1; i >= 0; i--) {
    if (body.messages[i]?.role === "user") { lastUser = body.messages[i]!; break; }
  }
  if (!lastUser) return false;
  const c = lastUser.content;
  const texts: string[] = [];
  if (typeof c === "string") texts.push(c);
  else if (Array.isArray(c)) for (const b of c) if (typeof b?.text === "string") texts.push(b.text);
  CTX_CMD_RE.lastIndex = 0;
  for (const t of texts) if (CTX_CMD_RE.test(t)) return true;
  return false;
}

export function ctxModeBlockedResponse(): Response {
  return new Response(
    JSON.stringify({
      type: "error",
      error: {
        type: "invalid_request_error",
        message: "context-mode disabled while fewtok is active. Run `ft off` and restart claude to use /ctx-* skills.",
      },
    }),
    { status: 400, headers: { "content-type": "application/json" } },
  );
}
